Add MaxImageResolution config setting (#17941)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e1b0644b0d
Коммит
96593580ae
@@ -542,6 +542,20 @@ func TestUploadFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: "Error image too large",
|
||||||
|
names: []string{"test.png"},
|
||||||
|
skipSuccessValidation: true,
|
||||||
|
checkResponse: CheckBadRequestStatus,
|
||||||
|
setupConfig: func(a *app.App) func(a *app.App) {
|
||||||
|
maxResSize := *a.Config().FileSettings.MaxImageResolution
|
||||||
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxImageResolution = 90000 })
|
||||||
|
return func(a *app.App) {
|
||||||
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxImageResolution = maxResSize })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, useMultipart := range []bool{true, false} {
|
for _, useMultipart := range []bool{true, false} {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (a *App) SaveBrandImage(imageData *multipart.FileHeader) *model.AppError {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
if err = checkImageLimits(file); err != nil {
|
if err = checkImageLimits(file, *a.Config().FileSettings.MaxImageResolution); err != nil {
|
||||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.check_image_limits.app_error", nil, err.Error(), http.StatusBadRequest)
|
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.check_image_limits.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxImageRes = int64(6048 * 4032) // 24 megapixels, up to ~196MB as a raw image
|
|
||||||
imageThumbnailWidth = 120
|
imageThumbnailWidth = 120
|
||||||
imageThumbnailHeight = 100
|
imageThumbnailHeight = 100
|
||||||
imagePreviewWidth = 1920
|
imagePreviewWidth = 1920
|
||||||
@@ -642,6 +641,7 @@ type UploadFileTask struct {
|
|||||||
teeInput io.Reader
|
teeInput io.Reader
|
||||||
fileinfo *model.FileInfo
|
fileinfo *model.FileInfo
|
||||||
maxFileSize int64
|
maxFileSize int64
|
||||||
|
maxImageRes int64
|
||||||
|
|
||||||
// Cached image data that (may) get initialized in preprocessImage and
|
// Cached image data that (may) get initialized in preprocessImage and
|
||||||
// is used in postprocessImage
|
// is used in postprocessImage
|
||||||
@@ -702,6 +702,7 @@ func (a *App) UploadFileX(c *request.Context, channelID, name string, input io.R
|
|||||||
Name: filepath.Base(name),
|
Name: filepath.Base(name),
|
||||||
Input: input,
|
Input: input,
|
||||||
maxFileSize: *a.Config().FileSettings.MaxFileSize,
|
maxFileSize: *a.Config().FileSettings.MaxFileSize,
|
||||||
|
maxImageRes: *a.Config().FileSettings.MaxImageResolution,
|
||||||
imgDecoder: a.srv.imgDecoder,
|
imgDecoder: a.srv.imgDecoder,
|
||||||
imgEncoder: a.srv.imgEncoder,
|
imgEncoder: a.srv.imgEncoder,
|
||||||
}
|
}
|
||||||
@@ -806,7 +807,7 @@ func (t *UploadFileTask) preprocessImage() *model.AppError {
|
|||||||
t.fileinfo.Width = w
|
t.fileinfo.Width = w
|
||||||
t.fileinfo.Height = h
|
t.fileinfo.Height = h
|
||||||
|
|
||||||
if err = checkImageResolutionLimit(w, h); err != nil {
|
if err = checkImageResolutionLimit(w, h, t.maxImageRes); err != nil {
|
||||||
return t.newAppError("api.file.upload_file.large_image_detailed.app_error", http.StatusBadRequest)
|
return t.newAppError("api.file.upload_file.large_image_detailed.app_error", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,7 +974,7 @@ func (a *App) DoUploadFileExpectModification(c *request.Context, now time.Time,
|
|||||||
info.Path = pathPrefix + filename
|
info.Path = pathPrefix + filename
|
||||||
|
|
||||||
if info.IsImage() {
|
if info.IsImage() {
|
||||||
if limitErr := checkImageResolutionLimit(info.Width, info.Height); limitErr != nil {
|
if limitErr := checkImageResolutionLimit(info.Width, info.Height, *a.Config().FileSettings.MaxImageResolution); limitErr != nil {
|
||||||
err := model.NewAppError("uploadFile", "api.file.upload_file.large_image.app_error", map[string]interface{}{"Filename": filename}, limitErr.Error(), http.StatusBadRequest)
|
err := model.NewAppError("uploadFile", "api.file.upload_file.large_image.app_error", map[string]interface{}{"Filename": filename}, limitErr.Error(), http.StatusBadRequest)
|
||||||
return nil, data, err
|
return nil, data, err
|
||||||
}
|
}
|
||||||
|
|||||||
10
app/image.go
10
app/image.go
@@ -10,23 +10,23 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/app/imaging"
|
"github.com/mattermost/mattermost-server/v6/app/imaging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkImageResolutionLimit(w, h int) error {
|
func checkImageResolutionLimit(w, h int, maxRes int64) error {
|
||||||
// This casting is done to prevent overflow on 32 bit systems (not needed
|
// This casting is done to prevent overflow on 32 bit systems (not needed
|
||||||
// in 64 bits systems because images can't have more than 32 bits height or
|
// in 64 bits systems because images can't have more than 32 bits height or
|
||||||
// width)
|
// width)
|
||||||
imageRes := int64(w) * int64(h)
|
imageRes := int64(w) * int64(h)
|
||||||
if imageRes > maxImageRes {
|
if imageRes > maxRes {
|
||||||
return fmt.Errorf("image resolution is too high: %d, max allowed is %d", imageRes, maxImageRes)
|
return fmt.Errorf("image resolution is too high: %d, max allowed is %d", imageRes, maxRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkImageLimits(imageData io.Reader) error {
|
func checkImageLimits(imageData io.Reader, maxRes int64) error {
|
||||||
w, h, err := imaging.GetDimensions(imageData)
|
w, h, err := imaging.GetDimensions(imageData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get image dimensions: %w", err)
|
return fmt.Errorf("failed to get image dimensions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkImageResolutionLimit(w, h)
|
return checkImageResolutionLimit(w, h, maxRes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1938,7 +1938,7 @@ func (a *App) SetTeamIconFromMultiPartFile(teamID string, file multipart.File) *
|
|||||||
return model.NewAppError("setTeamIcon", "api.team.set_team_icon.storage.app_error", nil, "", http.StatusNotImplemented)
|
return model.NewAppError("setTeamIcon", "api.team.set_team_icon.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
if limitErr := checkImageLimits(file); limitErr != nil {
|
if limitErr := checkImageLimits(file, *a.Config().FileSettings.MaxImageResolution); limitErr != nil {
|
||||||
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.check_image_limits.app_error",
|
return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.check_image_limits.app_error",
|
||||||
nil, limitErr.Error(), http.StatusBadRequest)
|
nil, limitErr.Error(), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ func (a *App) UploadData(c *request.Context, us *model.UploadSession, rd io.Read
|
|||||||
|
|
||||||
// image post-processing
|
// image post-processing
|
||||||
if info.IsImage() {
|
if info.IsImage() {
|
||||||
if limitErr := checkImageResolutionLimit(info.Width, info.Height); limitErr != nil {
|
if limitErr := checkImageResolutionLimit(info.Width, info.Height, *a.Config().FileSettings.MaxImageResolution); limitErr != nil {
|
||||||
return nil, model.NewAppError("uploadData", "app.upload.upload_data.large_image.app_error",
|
return nil, model.NewAppError("uploadData", "app.upload.upload_data.large_image.app_error",
|
||||||
map[string]interface{}{"Filename": us.Filename, "Width": info.Width, "Height": info.Height}, "", http.StatusBadRequest)
|
map[string]interface{}{"Filename": us.Filename, "Width": info.Width, "Height": info.Height}, "", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -753,7 +753,7 @@ func (a *App) SetProfileImage(userID string, imageData *multipart.FileHeader) *m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SetProfileImageFromMultiPartFile(userID string, file multipart.File) *model.AppError {
|
func (a *App) SetProfileImageFromMultiPartFile(userID string, file multipart.File) *model.AppError {
|
||||||
if limitErr := checkImageLimits(file); limitErr != nil {
|
if limitErr := checkImageLimits(file, *a.Config().FileSettings.MaxImageResolution); limitErr != nil {
|
||||||
return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.check_image_limits.app_error", nil, "", http.StatusBadRequest)
|
return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.check_image_limits.app_error", nil, "", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1403,6 +1403,7 @@ type FileSettings struct {
|
|||||||
EnableMobileUpload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"`
|
EnableMobileUpload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"`
|
||||||
EnableMobileDownload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"`
|
EnableMobileDownload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"`
|
||||||
MaxFileSize *int64 `access:"environment_file_storage,cloud_restrictable"`
|
MaxFileSize *int64 `access:"environment_file_storage,cloud_restrictable"`
|
||||||
|
MaxImageResolution *int64 `access:"environment_file_storage,cloud_restrictable"`
|
||||||
DriverName *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"`
|
DriverName *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"`
|
||||||
Directory *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"`
|
Directory *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"`
|
||||||
EnablePublicLink *bool `access:"site_public_links,cloud_restrictable"`
|
EnablePublicLink *bool `access:"site_public_links,cloud_restrictable"`
|
||||||
@@ -1436,7 +1437,11 @@ func (s *FileSettings) SetDefaults(isUpdate bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.MaxFileSize == nil {
|
if s.MaxFileSize == nil {
|
||||||
s.MaxFileSize = NewInt64(MB * 100)
|
s.MaxFileSize = NewInt64(100 * 1024 * 1024) // 100MB (IEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.MaxImageResolution == nil {
|
||||||
|
s.MaxImageResolution = NewInt64(7680 * 4320) // 8K, ~33MPX
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.DriverName == nil {
|
if s.DriverName == nil {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const (
|
|||||||
UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
NUMBERS = "0123456789"
|
NUMBERS = "0123456789"
|
||||||
SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
|
SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
|
||||||
MB = 1 << 20
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StringInterface map[string]interface{}
|
type StringInterface map[string]interface{}
|
||||||
|
|||||||
@@ -557,6 +557,7 @@ func (ts *TelemetryService) trackConfig() {
|
|||||||
"amazon_s3_signv2": *cfg.FileSettings.AmazonS3SignV2,
|
"amazon_s3_signv2": *cfg.FileSettings.AmazonS3SignV2,
|
||||||
"amazon_s3_trace": *cfg.FileSettings.AmazonS3Trace,
|
"amazon_s3_trace": *cfg.FileSettings.AmazonS3Trace,
|
||||||
"max_file_size": *cfg.FileSettings.MaxFileSize,
|
"max_file_size": *cfg.FileSettings.MaxFileSize,
|
||||||
|
"max_image_resolution": *cfg.FileSettings.MaxImageResolution,
|
||||||
"enable_file_attachments": *cfg.FileSettings.EnableFileAttachments,
|
"enable_file_attachments": *cfg.FileSettings.EnableFileAttachments,
|
||||||
"enable_mobile_upload": *cfg.FileSettings.EnableMobileUpload,
|
"enable_mobile_upload": *cfg.FileSettings.EnableMobileUpload,
|
||||||
"enable_mobile_download": *cfg.FileSettings.EnableMobileDownload,
|
"enable_mobile_download": *cfg.FileSettings.EnableMobileDownload,
|
||||||
|
|||||||
@@ -142,6 +142,7 @@
|
|||||||
"EnableMobileUpload": true,
|
"EnableMobileUpload": true,
|
||||||
"EnableMobileDownload": true,
|
"EnableMobileDownload": true,
|
||||||
"MaxFileSize": 52428800,
|
"MaxFileSize": 52428800,
|
||||||
|
"MaxImageResolution": 33177600,
|
||||||
"DriverName": "local",
|
"DriverName": "local",
|
||||||
"Directory": "./data/",
|
"Directory": "./data/",
|
||||||
"EnablePublicLink": false,
|
"EnablePublicLink": false,
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user