Limit total size of unsent attachments

This commit is contained in:
Drew DeVault 2020-11-19 09:27:59 -05:00
parent 7b3e580fe4
commit 297afc5ce6
3 changed files with 23 additions and 11 deletions

View file

@ -692,7 +692,13 @@ func handleComposeAttachment(ctx *alps.Context) error {
var uuids []string var uuids []string
for _, fh := range form.File["attachments"] { for _, fh := range form.File["attachments"] {
uuid, err := ctx.Session.PutAttachment(fh, form) uuid, err := ctx.Session.PutAttachment(fh, form)
if err != nil { if err == alps.ErrAttachmentCacheSize {
form.RemoveAll()
return ctx.JSON(http.StatusBadRequest, map[string]string{
"error": "The total size of unset attachments on your session exceeds the maximum file size. Remove some attachments and try again.",
})
} else if err != nil {
form.RemoveAll()
ctx.Logger().Printf("PutAttachment: %v\n", err) ctx.Logger().Printf("PutAttachment: %v\n", err)
return ctx.JSON(http.StatusBadRequest, map[string]string{ return ctx.JSON(http.StatusBadRequest, map[string]string{
"error": "failed to store attachment", "error": "failed to store attachment",

View file

@ -426,7 +426,7 @@ func New(e *echo.Echo, options *Options) (*Server, error) {
} }
ctx.Session, err = ctx.Server.Sessions.get(cookie.Value) ctx.Session, err = ctx.Server.Sessions.get(cookie.Value)
if err == errSessionExpired { if err == ErrSessionExpired {
ctx.SetSession(nil) ctx.SetSession(nil)
return handleUnauthenticated(next, ctx) return handleUnauthenticated(next, ctx)
} else if err != nil { } else if err != nil {

View file

@ -20,6 +20,7 @@ import (
// TODO: make this configurable // TODO: make this configurable
const sessionDuration = 30 * time.Minute const sessionDuration = 30 * time.Minute
const maxAttachmentSize = 32 << 20 // 32 MiB
func generateToken() (string, error) { func generateToken() (string, error) {
b := make([]byte, 32) b := make([]byte, 32)
@ -30,7 +31,10 @@ func generateToken() (string, error) {
return base64.URLEncoding.EncodeToString(b), nil return base64.URLEncoding.EncodeToString(b), nil
} }
var errSessionExpired = errors.New("session expired") var (
ErrSessionExpired = errors.New("session expired")
ErrAttachmentCacheSize = errors.New("Attachments on session exceed maximum file size")
)
// AuthError wraps an authentication error. // AuthError wraps an authentication error.
type AuthError struct { type AuthError struct {
@ -145,15 +149,17 @@ func (s *Session) Close() {
// Puts an attachment and returns a generated UUID // Puts an attachment and returns a generated UUID
func (s *Session) PutAttachment(in *multipart.FileHeader, func (s *Session) PutAttachment(in *multipart.FileHeader,
form *multipart.Form) (string, error) { form *multipart.Form) (string, error) {
// TODO: Prevent users from uploading too many attachments, or too large
//
// Probably just set a cap on the maximum combined size of all files in the
// user's session
//
// TODO: Figure out what to do if the user abandons the compose window
// after adding some attachments
id := uuid.New() id := uuid.New()
s.attachmentsLocker.Lock() s.attachmentsLocker.Lock()
var size int64
for _, a := range s.attachments {
size += a.File.Size
}
if size + in.Size > maxAttachmentSize {
return "", ErrAttachmentCacheSize
}
s.attachments[id.String()] = &Attachment{ s.attachments[id.String()] = &Attachment{
File: in, File: in,
Form: form, Form: form,
@ -241,7 +247,7 @@ func (sm *SessionManager) get(token string) (*Session, error) {
session, ok := sm.sessions[token] session, ok := sm.sessions[token]
if !ok { if !ok {
return nil, errSessionExpired return nil, ErrSessionExpired
} }
return session, nil return session, nil
} }