remove deprecated file extensions filters
these filters were deprecated a long time ago, everyone should use patterns filters now
This commit is contained in:
parent
402947a43c
commit
02bb09ec01
12 changed files with 64 additions and 372 deletions
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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{},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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, "")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
{{- 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}}
|
||||
{{- 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">
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue