remove deprecated file extensions filters

these filters were deprecated a long time ago, everyone should use
patterns filters now
This commit is contained in:
Nicola Murino 2021-05-22 12:28:05 +02:00
parent 402947a43c
commit 02bb09ec01
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
12 changed files with 64 additions and 372 deletions

View file

@ -1278,11 +1278,11 @@ func validatePublicKeys(user *User) error {
}
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
if err != nil {
return &ValidationError{err: fmt.Sprintf("could not parse key nr. %d: %s", i, err)}
return &ValidationError{err: fmt.Sprintf("could not parse key nr. %d: %s", i+1, err)}
}
validatedKeys = append(validatedKeys, k)
}
user.PublicKeys = validatedKeys
user.PublicKeys = utils.RemoveDuplicates(validatedKeys)
return nil
}
@ -1330,49 +1330,6 @@ func validateFiltersPatternExtensions(user *User) error {
return nil
}
func validateFiltersFileExtensions(user *User) error {
if len(user.Filters.FileExtensions) == 0 {
user.Filters.FileExtensions = []ExtensionsFilter{}
return nil
}
filteredPaths := []string{}
var filters []ExtensionsFilter
for _, f := range user.Filters.FileExtensions {
cleanedPath := filepath.ToSlash(path.Clean(f.Path))
if !path.IsAbs(cleanedPath) {
return &ValidationError{err: fmt.Sprintf("invalid path %#v for file extensions filter", f.Path)}
}
if utils.IsStringInSlice(cleanedPath, filteredPaths) {
return &ValidationError{err: fmt.Sprintf("duplicate file extensions filter for path %#v", f.Path)}
}
if len(f.AllowedExtensions) == 0 && len(f.DeniedExtensions) == 0 {
return &ValidationError{err: fmt.Sprintf("empty file extensions filter for path %#v", f.Path)}
}
f.Path = cleanedPath
allowed := make([]string, 0, len(f.AllowedExtensions))
denied := make([]string, 0, len(f.DeniedExtensions))
for _, ext := range f.AllowedExtensions {
allowed = append(allowed, strings.ToLower(ext))
}
for _, ext := range f.DeniedExtensions {
denied = append(denied, strings.ToLower(ext))
}
f.AllowedExtensions = allowed
f.DeniedExtensions = denied
filters = append(filters, f)
filteredPaths = append(filteredPaths, cleanedPath)
}
user.Filters.FileExtensions = filters
return nil
}
func validateFileFilters(user *User) error {
if err := validateFiltersFileExtensions(user); err != nil {
return err
}
return validateFiltersPatternExtensions(user)
}
func checkEmptyFiltersStruct(user *User) {
if len(user.Filters.AllowedIP) == 0 {
user.Filters.AllowedIP = []string{}
@ -1428,7 +1385,7 @@ func validateFilters(user *User) error {
return &ValidationError{err: fmt.Sprintf("invalid web client options %#v", opts)}
}
}
return validateFileFilters(user)
return validateFiltersPatternExtensions(user)
}
func saveGCSCredentials(fsConfig *vfs.Filesystem, helper fsValidatorHelper) error {

View file

@ -85,28 +85,6 @@ var (
errNoMatchingVirtualFolder = errors.New("no matching virtual folder found")
)
// ExtensionsFilter defines filters based on file extensions.
// These restrictions do not apply to files listing for performance reasons, so
// a denied file cannot be downloaded/overwritten/renamed but will still be
// in the list of files.
// System commands such as Git and rsync interacts with the filesystem directly
// and they are not aware about these restrictions so they are not allowed
// inside paths with extensions filters
type ExtensionsFilter struct {
// Virtual path, if no other specific filter is defined, the filter apply for
// sub directories too.
// For example if filters are defined for the paths "/" and "/sub" then the
// filters for "/" are applied for any file outside the "/sub" directory
Path string `json:"path"`
// only files with these, case insensitive, extensions are allowed.
// Shell like expansion is not supported so you have to specify ".jpg" and
// not "*.jpg". If you want shell like patterns use pattern filters
AllowedExtensions []string `json:"allowed_extensions,omitempty"`
// files with these, case insensitive, extensions are not allowed.
// Denied file extensions are evaluated before the allowed ones
DeniedExtensions []string `json:"denied_extensions,omitempty"`
}
// PatternsFilter defines filters based on shell like patterns.
// These restrictions do not apply to files listing for performance reasons, so
// a denied file cannot be downloaded/overwritten/renamed but will still be
@ -151,10 +129,8 @@ type UserFilters struct {
// these protocols are not allowed.
// If null or empty any available protocol is allowed
DeniedProtocols []string `json:"denied_protocols,omitempty"`
// filters based on file extensions.
// filter based on shell patterns.
// Please note that these restrictions can be easily bypassed.
FileExtensions []ExtensionsFilter `json:"file_extensions,omitempty"`
// filter based on shell patterns
FilePatterns []PatternsFilter `json:"file_patterns,omitempty"`
// max size allowed for a single upload, 0 means unlimited
MaxUploadFileSize int64 `json:"max_upload_file_size,omitempty"`
@ -780,41 +756,7 @@ func (u *User) GetAllowedLoginMethods() []string {
// IsFileAllowed returns true if the specified file is allowed by the file restrictions filters
func (u *User) IsFileAllowed(virtualPath string) bool {
return u.isFilePatternAllowed(virtualPath) && u.isFileExtensionAllowed(virtualPath)
}
func (u *User) isFileExtensionAllowed(virtualPath string) bool {
if len(u.Filters.FileExtensions) == 0 {
return true
}
dirsForPath := utils.GetDirsForVirtualPath(path.Dir(virtualPath))
var filter ExtensionsFilter
for _, dir := range dirsForPath {
for _, f := range u.Filters.FileExtensions {
if f.Path == dir {
filter = f
break
}
}
if filter.Path != "" {
break
}
}
if filter.Path != "" {
toMatch := strings.ToLower(virtualPath)
for _, denied := range filter.DeniedExtensions {
if strings.HasSuffix(toMatch, denied) {
return false
}
}
for _, allowed := range filter.AllowedExtensions {
if strings.HasSuffix(toMatch, allowed) {
return true
}
}
return len(filter.AllowedExtensions) == 0
}
return true
return u.isFilePatternAllowed(virtualPath)
}
func (u *User) isFilePatternAllowed(virtualPath string) bool {
@ -1113,8 +1055,6 @@ func (u *User) getACopy() User {
copy(filters.DeniedIP, u.Filters.DeniedIP)
filters.DeniedLoginMethods = make([]string, len(u.Filters.DeniedLoginMethods))
copy(filters.DeniedLoginMethods, u.Filters.DeniedLoginMethods)
filters.FileExtensions = make([]ExtensionsFilter, len(u.Filters.FileExtensions))
copy(filters.FileExtensions, u.Filters.FileExtensions)
filters.FilePatterns = make([]PatternsFilter, len(u.Filters.FilePatterns))
copy(filters.FilePatterns, u.Filters.FilePatterns)
filters.DeniedProtocols = make([]string, len(u.Filters.DeniedProtocols))

View file

@ -983,18 +983,11 @@ func TestDownloadErrors(t *testing.T) {
u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems}
u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
dataprovider.PermDelete, dataprovider.PermDownload}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
{
Path: "/sub2",
AllowedExtensions: []string{},
DeniedExtensions: []string{".zip"},
},
}
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/sub2",
AllowedPatterns: []string{},
DeniedPatterns: []string{"*.jpg"},
DeniedPatterns: []string{"*.jpg", "*.zip"},
},
}
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
@ -1042,11 +1035,11 @@ func TestUploadErrors(t *testing.T) {
u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems}
u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
dataprovider.PermDelete}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/sub2",
AllowedExtensions: []string{},
DeniedExtensions: []string{".zip"},
Path: "/sub2",
AllowedPatterns: []string{},
DeniedPatterns: []string{"*.zip"},
},
}
user, _, err := httpdtest.AddUser(u, http.StatusCreated)

View file

@ -645,39 +645,38 @@ func TestAddUserInvalidFilters(t *testing.T) {
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.Filters.DeniedLoginMethods = []string{}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "relative",
AllowedExtensions: []string{},
DeniedExtensions: []string{},
Path: "relative",
AllowedPatterns: []string{},
DeniedPatterns: []string{},
},
}
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/",
AllowedExtensions: []string{},
DeniedExtensions: []string{},
Path: "/",
AllowedPatterns: []string{},
DeniedPatterns: []string{},
},
}
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/subdir",
AllowedExtensions: []string{".zip"},
DeniedExtensions: []string{},
Path: "/subdir",
AllowedPatterns: []string{"*.zip"},
DeniedPatterns: []string{},
},
{
Path: "/subdir",
AllowedExtensions: []string{".rar"},
DeniedExtensions: []string{".jpg"},
Path: "/subdir",
AllowedPatterns: []string{"*.rar"},
DeniedPatterns: []string{"*.jpg"},
},
}
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.Filters.FileExtensions = nil
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "relative",
@ -1140,11 +1139,6 @@ func TestUpdateUser(t *testing.T) {
user.Filters.Hooks.PreLoginDisabled = true
user.Filters.Hooks.CheckPasswordDisabled = false
user.Filters.DisableFsChecks = true
user.Filters.FileExtensions = append(user.Filters.FileExtensions, dataprovider.ExtensionsFilter{
Path: "/subdir",
AllowedExtensions: []string{".zip", ".rar"},
DeniedExtensions: []string{".jpg", ".png"},
})
user.Filters.FilePatterns = append(user.Filters.FilePatterns, dataprovider.PatternsFilter{
Path: "/subdir",
AllowedPatterns: []string{"*.zip", "*.rar"},
@ -5907,8 +5901,6 @@ func TestWebUserAddMock(t *testing.T) {
form.Set("permissions", "*")
form.Set("sub_dirs_permissions", " /subdir::list ,download ")
form.Set("virtual_folders", fmt.Sprintf(" /vdir:: %v :: 2 :: 1024", folderName))
form.Set("allowed_extensions", "/dir2::.jpg,.png\n/dir2::.ico\n/dir1::.rar")
form.Set("denied_extensions", "/dir2::.webp,.webp\n/dir2::.tiff\n/dir1::.zip")
form.Set("allowed_patterns", "/dir2::*.jpg,*.png\n/dir1::*.png")
form.Set("denied_patterns", "/dir1::*.zip\n/dir3::*.rar\n/dir2::*.mkv")
form.Set("additional_info", user.AdditionalInfo)
@ -6093,24 +6085,6 @@ func TestWebUserAddMock(t *testing.T) {
assert.Equal(t, v.QuotaFiles, 2)
assert.Equal(t, v.QuotaSize, int64(1024))
}
assert.Len(t, newUser.Filters.FileExtensions, 2)
for _, filter := range newUser.Filters.FileExtensions {
if filter.Path == "/dir1" {
assert.Len(t, filter.DeniedExtensions, 1)
assert.Len(t, filter.AllowedExtensions, 1)
assert.True(t, utils.IsStringInSlice(".zip", filter.DeniedExtensions))
assert.True(t, utils.IsStringInSlice(".rar", filter.AllowedExtensions))
}
if filter.Path == "/dir2" {
assert.Len(t, filter.DeniedExtensions, 2)
assert.Len(t, filter.AllowedExtensions, 3)
assert.True(t, utils.IsStringInSlice(".jpg", filter.AllowedExtensions))
assert.True(t, utils.IsStringInSlice(".png", filter.AllowedExtensions))
assert.True(t, utils.IsStringInSlice(".ico", filter.AllowedExtensions))
assert.True(t, utils.IsStringInSlice(".webp", filter.DeniedExtensions))
assert.True(t, utils.IsStringInSlice(".tiff", filter.DeniedExtensions))
}
}
assert.Len(t, newUser.Filters.FilePatterns, 3)
for _, filter := range newUser.Filters.FilePatterns {
if filter.Path == "/dir1" {
@ -6185,7 +6159,7 @@ func TestWebUserUpdateMock(t *testing.T) {
form.Set("expiration_date", "2020-01-01 00:00:00")
form.Set("allowed_ip", " 192.168.1.3/32, 192.168.2.0/24 ")
form.Set("denied_ip", " 10.0.0.2/32 ")
form.Set("denied_extensions", "/dir1::.zip")
form.Set("denied_patterns", "/dir1::*.zip")
form.Set("ssh_login_methods", dataprovider.SSHLoginMethodKeyboardInteractive)
form.Set("denied_protocols", common.ProtocolFTP)
form.Set("max_upload_file_size", "100")
@ -6267,7 +6241,7 @@ func TestWebUserUpdateMock(t *testing.T) {
assert.True(t, utils.IsStringInSlice("10.0.0.2/32", updateUser.Filters.DeniedIP))
assert.True(t, utils.IsStringInSlice(dataprovider.SSHLoginMethodKeyboardInteractive, updateUser.Filters.DeniedLoginMethods))
assert.True(t, utils.IsStringInSlice(common.ProtocolFTP, updateUser.Filters.DeniedProtocols))
assert.True(t, utils.IsStringInSlice(".zip", updateUser.Filters.FileExtensions[0].DeniedExtensions))
assert.True(t, utils.IsStringInSlice("*.zip", updateUser.Filters.FilePatterns[0].DeniedPatterns))
req, err = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
assert.NoError(t, err)
setBearerForReq(req, apiToken)
@ -6737,8 +6711,8 @@ func TestWebUserS3Mock(t *testing.T) {
form.Set("s3_storage_class", user.FsConfig.S3Config.StorageClass)
form.Set("s3_endpoint", user.FsConfig.S3Config.Endpoint)
form.Set("s3_key_prefix", user.FsConfig.S3Config.KeyPrefix)
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir2::.zip")
form.Set("allowed_patterns", "/dir1::*.jpg,*.png")
form.Set("denied_patterns", "/dir2::*.zip")
form.Set("max_upload_file_size", "0")
form.Set("description", user.Description)
form.Add("hooks", "pre_login_disabled")
@ -6783,7 +6757,7 @@ func TestWebUserS3Mock(t *testing.T) {
assert.Equal(t, updateUser.FsConfig.S3Config.KeyPrefix, user.FsConfig.S3Config.KeyPrefix)
assert.Equal(t, updateUser.FsConfig.S3Config.UploadPartSize, user.FsConfig.S3Config.UploadPartSize)
assert.Equal(t, updateUser.FsConfig.S3Config.UploadConcurrency, user.FsConfig.S3Config.UploadConcurrency)
assert.Equal(t, 2, len(updateUser.Filters.FileExtensions))
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, updateUser.FsConfig.S3Config.AccessSecret.GetKey())
@ -6882,7 +6856,7 @@ func TestWebUserGCSMock(t *testing.T) {
form.Set("gcs_bucket", user.FsConfig.GCSConfig.Bucket)
form.Set("gcs_storage_class", user.FsConfig.GCSConfig.StorageClass)
form.Set("gcs_key_prefix", user.FsConfig.GCSConfig.KeyPrefix)
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("allowed_patterns", "/dir1::*.jpg,*.png")
form.Set("max_upload_file_size", "0")
b, contentType, _ := getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
@ -6916,7 +6890,7 @@ func TestWebUserGCSMock(t *testing.T) {
assert.Equal(t, user.FsConfig.GCSConfig.Bucket, updateUser.FsConfig.GCSConfig.Bucket)
assert.Equal(t, user.FsConfig.GCSConfig.StorageClass, updateUser.FsConfig.GCSConfig.StorageClass)
assert.Equal(t, user.FsConfig.GCSConfig.KeyPrefix, updateUser.FsConfig.GCSConfig.KeyPrefix)
assert.Equal(t, "/dir1", updateUser.Filters.FileExtensions[0].Path)
assert.Equal(t, "/dir1", updateUser.Filters.FilePatterns[0].Path)
form.Set("gcs_auto_credentials", "on")
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
@ -6989,8 +6963,8 @@ func TestWebUserAzureBlobMock(t *testing.T) {
form.Set("az_endpoint", user.FsConfig.AzBlobConfig.Endpoint)
form.Set("az_key_prefix", user.FsConfig.AzBlobConfig.KeyPrefix)
form.Set("az_use_emulator", "checked")
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir2::.zip")
form.Set("allowed_patterns", "/dir1::*.jpg,*.png")
form.Set("denied_patterns", "/dir2::*.zip")
form.Set("max_upload_file_size", "0")
// test invalid az_upload_part_size
form.Set("az_upload_part_size", "a")
@ -7032,7 +7006,7 @@ func TestWebUserAzureBlobMock(t *testing.T) {
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.KeyPrefix, user.FsConfig.AzBlobConfig.KeyPrefix)
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadPartSize, user.FsConfig.AzBlobConfig.UploadPartSize)
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadConcurrency, user.FsConfig.AzBlobConfig.UploadConcurrency)
assert.Equal(t, 2, len(updateUser.Filters.FileExtensions))
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetKey())
@ -7099,8 +7073,8 @@ func TestWebUserCryptMock(t *testing.T) {
form.Set("denied_ip", "")
form.Set("fs_provider", "4")
form.Set("crypt_passphrase", "")
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir2::.zip")
form.Set("allowed_patterns", "/dir1::*.jpg,*.png")
form.Set("denied_patterns", "/dir2::*.zip")
form.Set("max_upload_file_size", "0")
// passphrase cannot be empty
b, contentType, _ := getMultipartFormData(form, "", "")
@ -7124,7 +7098,7 @@ func TestWebUserCryptMock(t *testing.T) {
err = render.DecodeJSON(rr.Body, &updateUser)
assert.NoError(t, err)
assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
assert.Equal(t, 2, len(updateUser.Filters.FileExtensions))
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetKey())
@ -7198,8 +7172,8 @@ func TestWebUserSFTPFsMock(t *testing.T) {
form.Set("denied_ip", "")
form.Set("fs_provider", "5")
form.Set("crypt_passphrase", "")
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir2::.zip")
form.Set("allowed_patterns", "/dir1::*.jpg,*.png")
form.Set("denied_patterns", "/dir2::*.zip")
form.Set("max_upload_file_size", "0")
// empty sftpconfig
b, contentType, _ := getMultipartFormData(form, "", "")
@ -7230,7 +7204,7 @@ func TestWebUserSFTPFsMock(t *testing.T) {
err = render.DecodeJSON(rr.Body, &updateUser)
assert.NoError(t, err)
assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
assert.Equal(t, 2, len(updateUser.Filters.FileExtensions))
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.Password.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.SFTPConfig.Password.GetPayload())
assert.Empty(t, updateUser.FsConfig.SFTPConfig.Password.GetKey())

View file

@ -1414,27 +1414,6 @@ components:
description: 'list of, case insensitive, denied shell like file patterns. Denied patterns are evaluated before the allowed ones'
example:
- '*.zip'
ExtensionsFilter:
type: object
properties:
path:
type: string
description: 'exposed virtual path, if no other specific filter is defined, the filter apply for sub directories too. For example if filters are defined for the paths "/" and "/sub" then the filters for "/" are applied for any file outside the "/sub" directory'
allowed_extensions:
type: array
items:
type: string
description: 'list of, case insensitive, allowed files extension. Shell like expansion is not supported so you have to specify `.jpg` and not `*.jpg`'
example:
- .jpg
- .png
denied_extensions:
type: array
items:
type: string
description: 'list of, case insensitive, denied files extension. Denied file extensions are evaluated before the allowed ones'
example:
- .zip
HooksFilter:
type: object
properties:
@ -1484,11 +1463,6 @@ components:
items:
$ref: '#/components/schemas/PatternsFilter'
description: 'filters based on shell like file patterns. These restrictions do not apply to files listing for performance reasons, so a denied file cannot be downloaded/overwritten/renamed but it will still be in the list of files. Please note that these restrictions can be easily bypassed'
file_extensions:
type: array
items:
$ref: '#/components/schemas/ExtensionsFilter'
description: 'filters based on shell like patterns. Deprecated, use file_patterns. These restrictions do not apply to files listing for performance reasons, so a denied file cannot be downloaded/overwritten/renamed but it will still be in the list of files. Please note that these restrictions can be easily bypassed'
max_upload_file_size:
type: integer
format: int64

View file

@ -587,49 +587,12 @@ func getFilePatternsFromPostField(valueAllowed, valuesDenied string) []dataprovi
return result
}
func getFileExtensionsFromPostField(valueAllowed, valuesDenied string) []dataprovider.ExtensionsFilter {
var result []dataprovider.ExtensionsFilter
allowedExtensions := getListFromPostFields(valueAllowed)
deniedExtensions := getListFromPostFields(valuesDenied)
for dirAllowed, allowedExts := range allowedExtensions {
filter := dataprovider.ExtensionsFilter{
Path: dirAllowed,
AllowedExtensions: allowedExts,
}
for dirDenied, deniedExts := range deniedExtensions {
if dirAllowed == dirDenied {
filter.DeniedExtensions = deniedExts
break
}
}
result = append(result, filter)
}
for dirDenied, deniedExts := range deniedExtensions {
found := false
for _, res := range result {
if res.Path == dirDenied {
found = true
break
}
}
if !found {
result = append(result, dataprovider.ExtensionsFilter{
Path: dirDenied,
DeniedExtensions: deniedExts,
})
}
}
return result
}
func getFiltersFromUserPostFields(r *http.Request) dataprovider.UserFilters {
var filters dataprovider.UserFilters
filters.AllowedIP = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
filters.DeniedIP = getSliceFromDelimitedValues(r.Form.Get("denied_ip"), ",")
filters.DeniedLoginMethods = r.Form["ssh_login_methods"]
filters.DeniedProtocols = r.Form["denied_protocols"]
filters.FileExtensions = getFileExtensionsFromPostField(r.Form.Get("allowed_extensions"), r.Form.Get("denied_extensions"))
filters.FilePatterns = getFilePatternsFromPostField(r.Form.Get("allowed_patterns"), r.Form.Get("denied_patterns"))
filters.TLSUsername = dataprovider.TLSUsername(r.Form.Get("tls_username"))
filters.WebClient = r.Form["web_client_options"]

View file

@ -1213,9 +1213,6 @@ func compareUserFilters(expected *dataprovider.User, actual *dataprovider.User)
if err := compareUserFilterSubStructs(expected, actual); err != nil {
return err
}
if err := compareUserFileExtensionsFilters(expected, actual); err != nil {
return err
}
return compareUserFilePatternsFilters(expected, actual)
}
@ -1253,28 +1250,6 @@ func compareUserFilePatternsFilters(expected *dataprovider.User, actual *datapro
return nil
}
func compareUserFileExtensionsFilters(expected *dataprovider.User, actual *dataprovider.User) error {
if len(expected.Filters.FileExtensions) != len(actual.Filters.FileExtensions) {
return errors.New("file extensions mismatch")
}
for _, f := range expected.Filters.FileExtensions {
found := false
for _, f1 := range actual.Filters.FileExtensions {
if path.Clean(f.Path) == path.Clean(f1.Path) {
if !checkFilterMatch(f.AllowedExtensions, f1.AllowedExtensions) ||
!checkFilterMatch(f.DeniedExtensions, f1.DeniedExtensions) {
return errors.New("file extensions contents mismatch")
}
found = true
}
}
if !found {
return errors.New("file extensions contents mismatch")
}
}
return nil
}
func compareEqualsUserFields(expected *dataprovider.User, actual *dataprovider.User) error {
if expected.Username != actual.Username {
return errors.New("username mismatch")

View file

@ -592,11 +592,11 @@ func TestCommandsWithExtensionsFilter(t *testing.T) {
HomeDir: os.TempDir(),
Status: 1,
}
user.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
user.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/subdir",
AllowedExtensions: []string{".jpg"},
DeniedExtensions: []string{},
Path: "/subdir",
AllowedPatterns: []string{".jpg"},
DeniedPatterns: []string{},
},
}

View file

@ -3534,13 +3534,6 @@ func TestExtensionsFilters(t *testing.T) {
err = sftpUploadFile(testFilePath, testFileName+".jpg", testFileSize, client)
assert.NoError(t, err)
}
user.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
{
Path: "/",
AllowedExtensions: []string{".zIp", ".jPg"},
DeniedExtensions: []string{},
},
}
user.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/",
@ -6760,7 +6753,6 @@ func TestUserPerms(t *testing.T) {
assert.True(t, user.HasPerm(dataprovider.PermDownload, "/p/1/test/file.dat"))
}
//nolint:dupl
func TestFilterFilePatterns(t *testing.T) {
user := getTestUser(true)
pattern := dataprovider.PatternsFilter{
@ -6797,43 +6789,6 @@ func TestFilterFilePatterns(t *testing.T) {
assert.False(t, user.IsFileAllowed("/test/test.zip"))
}
//nolint:dupl
func TestFilterFileExtensions(t *testing.T) {
user := getTestUser(true)
extension := dataprovider.ExtensionsFilter{
Path: "/test",
AllowedExtensions: []string{".jpg", ".png"},
DeniedExtensions: []string{".pdf"},
}
filters := dataprovider.UserFilters{
FileExtensions: []dataprovider.ExtensionsFilter{extension},
}
user.Filters = filters
assert.True(t, user.IsFileAllowed("/test/test.jPg"))
assert.False(t, user.IsFileAllowed("/test/test.pdf"))
assert.True(t, user.IsFileAllowed("/test.pDf"))
filters.FileExtensions = append(filters.FileExtensions, dataprovider.ExtensionsFilter{
Path: "/",
AllowedExtensions: []string{".zip", ".rar", ".pdf"},
DeniedExtensions: []string{".gz"},
})
user.Filters = filters
assert.False(t, user.IsFileAllowed("/test1/test.gz"))
assert.True(t, user.IsFileAllowed("/test1/test.zip"))
assert.False(t, user.IsFileAllowed("/test/sub/test.pdf"))
assert.False(t, user.IsFileAllowed("/test1/test.png"))
filters.FileExtensions = append(filters.FileExtensions, dataprovider.ExtensionsFilter{
Path: "/test/sub",
DeniedExtensions: []string{".tar"},
})
user.Filters = filters
assert.False(t, user.IsFileAllowed("/test/sub/sub/test.tar"))
assert.True(t, user.IsFileAllowed("/test/sub/test.gz"))
assert.False(t, user.IsFileAllowed("/test/test.zip"))
}
func TestUserAllowedLoginMethods(t *testing.T) {
user := getTestUser(true)
user.Filters.DeniedLoginMethods = dataprovider.ValidLoginMethods
@ -7286,10 +7241,10 @@ func TestSSHCopy(t *testing.T) {
QuotaFiles: 100,
QuotaSize: 0,
})
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/",
DeniedExtensions: []string{".denied"},
Path: "/",
DeniedPatterns: []string{"*.denied"},
},
}
err := os.MkdirAll(mappedPath1, os.ModePerm)
@ -7564,10 +7519,10 @@ func TestSSHCopyQuotaLimits(t *testing.T) {
QuotaFiles: 3,
QuotaSize: testFileSize + testFileSize1 + 1,
})
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/",
DeniedExtensions: []string{".denied"},
Path: "/",
DeniedPatterns: []string{"*.denied"},
},
}
err := os.MkdirAll(mappedPath1, os.ModePerm)
@ -8327,7 +8282,7 @@ func TestSCPRecursive(t *testing.T) {
assert.NoError(t, err)
}
func TestSCPExtensionsFilter(t *testing.T) {
func TestSCPPatternsFilter(t *testing.T) {
if len(scpPath) == 0 {
t.Skip("scp command not found, unable to execute this test")
}
@ -8344,11 +8299,11 @@ func TestSCPExtensionsFilter(t *testing.T) {
assert.NoError(t, err)
err = scpUpload(testFilePath, remoteUpPath, false, false)
assert.NoError(t, err)
user.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
user.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/",
AllowedExtensions: []string{".zip"},
DeniedExtensions: []string{},
Path: "/",
AllowedPatterns: []string{"*.zip"},
DeniedPatterns: []string{},
},
}
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")

View file

@ -415,17 +415,17 @@ func (c *sshCommand) isSystemCommandAllowed() error {
c.command, sshDestPath, c.connection.User.Username)
return errUnsupportedConfig
}
for _, f := range c.connection.User.Filters.FileExtensions {
for _, f := range c.connection.User.Filters.FilePatterns {
if f.Path == sshDestPath {
c.connection.Log(logger.LevelDebug,
"command %#v is not allowed inside folders with files extensions filters %#v user %#v",
"command %#v is not allowed inside folders with files patterns filters %#v user %#v",
c.command, sshDestPath, c.connection.User.Username)
return errUnsupportedConfig
}
if len(sshDestPath) > len(f.Path) {
if strings.HasPrefix(sshDestPath, f.Path+"/") || f.Path == "/" {
c.connection.Log(logger.LevelDebug,
"command %#v is not allowed it includes folders with files extensions filters %#v user %#v",
"command %#v is not allowed it includes folders with files patterns filters %#v user %#v",
c.command, sshDestPath, c.connection.User.Username)
return errUnsupportedConfig
}

View file

@ -339,38 +339,6 @@
</div>
</div>
<div class="form-group row">
<label for="idFilesExtensionsDenied" class="col-sm-2 col-form-label">Denied file extensions</label>
<div class="col-sm-10">
<textarea class="form-control" id="idFilesExtensionsDenied" name="denied_extensions" rows="3"
aria-describedby="deniedExtensionsHelpBlock">{{range $index, $filter := .User.Filters.FileExtensions -}}
{{if $filter.DeniedExtensions -}}
{{$filter.Path}}::{{range $idx, $p := $filter.DeniedExtensions}}{{if $idx}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="deniedExtensionsHelpBlock" class="form-text text-muted">
One exposed virtual directory per line as /dir::extension1,extension2, for example
/subdir::.zip,.rar. Deprecated, use file patterns
</small>
</div>
</div>
<div class="form-group row">
<label for="idFilesExtensionsAllowed" class="col-sm-2 col-form-label">Allowed file extensions</label>
<div class="col-sm-10">
<textarea class="form-control" id="idFilesExtensionsAllowed" name="allowed_extensions" rows="3"
aria-describedby="allowedExtensionsHelpBlock">{{range $index, $filter := .User.Filters.FileExtensions -}}
{{if $filter.AllowedExtensions -}}
{{$filter.Path}}::{{range $idx, $p := $filter.AllowedExtensions}}{{if $idx}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="allowedExtensionsHelpBlock" class="form-text text-muted">
One exposed virtual directory per line as /dir::extension1,extension2, for example
/somedir::.jpg,.png. Deprecated, use file patterns
</small>
</div>
</div>
<div class="form-group row">
<label for="idHooks" class="col-sm-2 col-form-label">Hooks</label>
<div class="col-sm-10">

View file

@ -1022,18 +1022,11 @@ func TestDownloadErrors(t *testing.T) {
u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
dataprovider.PermDelete, dataprovider.PermDownload}
// use an unknown mime to trigger content type detection
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
{
Path: "/sub2",
AllowedExtensions: []string{},
DeniedExtensions: []string{".zipp"},
},
}
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/sub2",
AllowedPatterns: []string{},
DeniedPatterns: []string{"*.jpg"},
DeniedPatterns: []string{"*.jpg", "*.zipp"},
},
}
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
@ -1079,11 +1072,11 @@ func TestUploadErrors(t *testing.T) {
u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems, dataprovider.PermDownload}
u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
dataprovider.PermDelete, dataprovider.PermDownload}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
u.Filters.FilePatterns = []dataprovider.PatternsFilter{
{
Path: "/sub2",
AllowedExtensions: []string{},
DeniedExtensions: []string{".zip"},
Path: "/sub2",
AllowedPatterns: []string{},
DeniedPatterns: []string{"*.zip"},
},
}
user, _, err := httpdtest.AddUser(u, http.StatusCreated)