file patterns: evaluate allowed filters before the denied ones

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2023-05-24 19:56:53 +02:00
parent 2238043efd
commit f8f8962ccb
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
6 changed files with 165 additions and 7 deletions

2
go.mod
View file

@ -53,7 +53,7 @@ require (
github.com/rs/cors v1.9.0 github.com/rs/cors v1.9.0
github.com/rs/xid v1.5.0 github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.29.1 github.com/rs/zerolog v1.29.1
github.com/sftpgo/sdk v0.1.4 github.com/sftpgo/sdk v0.1.5-0.20230524172149-afb96ebee860
github.com/shirou/gopsutil/v3 v3.23.4 github.com/shirou/gopsutil/v3 v3.23.4
github.com/spf13/afero v1.9.5 github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0

4
go.sum
View file

@ -1842,8 +1842,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo= github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=
github.com/sftpgo/sdk v0.1.4 h1:TyNzpG7o8PuOgAQ1AvpFNJ93quR/90r+9FYyElTKqfw= github.com/sftpgo/sdk v0.1.5-0.20230524172149-afb96ebee860 h1:adaUl1JO/4bPhQuhSH7bQJ2o+2CW6Ry7R2w2SltS/PE=
github.com/sftpgo/sdk v0.1.4/go.mod h1:TjeoMWS0JEXt9RukJveTnaiHj4+MVLtUiDC+mY++Odk= github.com/sftpgo/sdk v0.1.5-0.20230524172149-afb96ebee860/go.mod h1:TjeoMWS0JEXt9RukJveTnaiHj4+MVLtUiDC+mY++Odk=
github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o= github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8= github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=

View file

@ -16,6 +16,7 @@ package common
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -645,3 +646,160 @@ func TestFsFileCopier(t *testing.T) {
_, ok = fs.(vfs.FsFileCopier) _, ok = fs.(vfs.FsFileCopier)
assert.True(t, ok) assert.True(t, ok)
} }
func TestFilterListDirs(t *testing.T) {
filters := dataprovider.UserFilters{
BaseUserFilters: sdk.BaseUserFilters{
FilePatterns: []sdk.PatternsFilter{
{
Path: "/dir1",
DenyPolicy: sdk.DenyPolicyDefault,
AllowedPatterns: []string{"*.jpg"},
},
{
Path: "/dir2",
DenyPolicy: sdk.DenyPolicyHide,
AllowedPatterns: []string{"*.jpg"},
},
},
},
}
virtualFolders := []vfs.VirtualFolder{
{
VirtualPath: "/dir1/vdir1",
},
{
VirtualPath: "/dir1/vdir2",
},
{
VirtualPath: "/dir1/vdir3",
},
{
VirtualPath: "/dir2/vdir1",
},
{
VirtualPath: "/dir2/vdir2",
},
{
VirtualPath: "/dir2/vdir3.jpg",
},
}
user := dataprovider.User{
Filters: filters,
VirtualFolders: virtualFolders,
}
dirContents := []os.FileInfo{
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
}
filtered := user.FilterListDir(dirContents, "/dir1")
assert.Len(t, filtered, 5)
filtered = user.FilterListDir(dirContents, "/dir2")
assert.Len(t, filtered, 2)
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir1")
assert.Len(t, filtered, 5)
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir2")
if assert.Len(t, filtered, 1) {
assert.True(t, filtered[0].IsDir())
}
user.VirtualFolders = nil
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir1")
assert.Len(t, filtered, 2)
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir2")
if assert.Len(t, filtered, 1) {
assert.False(t, filtered[0].IsDir())
}
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir2")
if assert.Len(t, filtered, 2) {
assert.False(t, filtered[0].IsDir())
assert.False(t, filtered[1].IsDir())
}
user.VirtualFolders = virtualFolders
user.Filters = filters
filtered = user.FilterListDir(nil, "/dir1")
assert.Len(t, filtered, 3)
filtered = user.FilterListDir(nil, "/dir2")
assert.Len(t, filtered, 1)
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.jPg", false, 123, time.Now(), false),
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 456, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir2")
assert.Len(t, filtered, 2)
user = dataprovider.User{
Filters: dataprovider.UserFilters{
BaseUserFilters: sdk.BaseUserFilters{
FilePatterns: []sdk.PatternsFilter{
{
Path: "/dir3",
AllowedPatterns: []string{"ic35"},
DeniedPatterns: []string{"*"},
DenyPolicy: sdk.DenyPolicyHide,
},
},
},
},
}
dirContents = []os.FileInfo{
vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
vfs.NewFileInfo("vdir3.jpg", false, 456, time.Now(), false),
}
filtered = user.FilterListDir(dirContents, "/dir3")
assert.Len(t, filtered, 0)
dirContents = nil
for i := 0; i < 100; i++ {
dirContents = append(dirContents, vfs.NewFileInfo(fmt.Sprintf("ic%02d", i), i%2 == 0, int64(i), time.Now(), false))
}
dirContents = append(dirContents, vfs.NewFileInfo("ic350", false, 123, time.Now(), false))
dirContents = append(dirContents, vfs.NewFileInfo(".ic35", false, 123, time.Now(), false))
dirContents = append(dirContents, vfs.NewFileInfo("ic35.", false, 123, time.Now(), false))
dirContents = append(dirContents, vfs.NewFileInfo("*ic35", false, 123, time.Now(), false))
dirContents = append(dirContents, vfs.NewFileInfo("ic35*", false, 123, time.Now(), false))
dirContents = append(dirContents, vfs.NewFileInfo("ic35.*", false, 123, time.Now(), false))
dirContents = append(dirContents, vfs.NewFileInfo("file.jpg", false, 123, time.Now(), false))
filtered = user.FilterListDir(dirContents, "/dir3")
if assert.Len(t, filtered, 1) {
assert.Equal(t, "ic35", filtered[0].Name())
}
}

View file

@ -5403,7 +5403,7 @@ components:
type: array type: array
items: items:
type: string type: string
description: 'list of, case insensitive, allowed shell like patterns.' description: 'list of, case insensitive, allowed shell like patterns. Allowed patterns are evaluated before the denied ones'
example: example:
- '*.jpg' - '*.jpg'
- a*b?.png - a*b?.png
@ -5411,7 +5411,7 @@ components:
type: array type: array
items: items:
type: string type: string
description: 'list of, case insensitive, denied shell like patterns. Denied patterns are evaluated before the allowed ones' description: 'list of, case insensitive, denied shell like patterns'
example: example:
- '*.zip' - '*.zip'
deny_policy: deny_policy:

View file

@ -222,7 +222,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title mb-4">Comma separated denied or allowed files/directories, based on shell patterns.</h6> <h6 class="card-title mb-4">Comma separated denied or allowed files/directories, based on shell patterns.</h6>
<p class="card-text">Denied entries are visible in directory listing by default, you can hide them by setting the "Hidden" policy, but please be aware that this may cause performance issues for large directories</p> <p class="card-text">Match is case insensitive, set you patterns as lowercase. Denied entries are visible in directory listing by default, you can hide them by setting the "Hidden" policy, but please be aware that this may cause performance issues for large directories</p>
<div class="form-group row"> <div class="form-group row">
<div class="col-md-12 form_field_patterns_outer"> <div class="col-md-12 form_field_patterns_outer">
{{range $idx, $pattern := .Group.UserSettings.Filters.GetFlatFilePatterns -}} {{range $idx, $pattern := .Group.UserSettings.Filters.GetFlatFilePatterns -}}

View file

@ -502,7 +502,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title mb-4">Comma separated denied or allowed files/directories, based on shell patterns.</h6> <h6 class="card-title mb-4">Comma separated denied or allowed files/directories, based on shell patterns.</h6>
<p class="card-text">Denied entries are visible in directory listing by default, you can hide them by setting the "Hidden" policy, but please be aware that this may cause performance issues for large directories</p> <p class="card-text">Match is case insensitive, set you patterns as lowercase. Denied entries are visible in directory listing by default, you can hide them by setting the "Hidden" policy, but please be aware that this may cause performance issues for large directories</p>
<div class="form-group row"> <div class="form-group row">
<div class="col-md-12 form_field_patterns_outer"> <div class="col-md-12 form_field_patterns_outer">
{{range $idx, $pattern := .User.Filters.GetFlatFilePatterns -}} {{range $idx, $pattern := .User.Filters.GetFlatFilePatterns -}}