mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 09:00:27 +00:00
file patterns: fix denied except rules
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
7f65aa1fa4
commit
c457538280
4 changed files with 249 additions and 13 deletions
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/rs/xid"
|
||||
"github.com/sftpgo/sdk"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/kms"
|
||||
|
@ -647,7 +648,7 @@ func TestFsFileCopier(t *testing.T) {
|
|||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func TestFilterListDirs(t *testing.T) {
|
||||
func TestFilePatterns(t *testing.T) {
|
||||
filters := dataprovider.UserFilters{
|
||||
BaseUserFilters: sdk.BaseUserFilters{
|
||||
FilePatterns: []sdk.PatternsFilter{
|
||||
|
@ -661,6 +662,16 @@ func TestFilterListDirs(t *testing.T) {
|
|||
DenyPolicy: sdk.DenyPolicyHide,
|
||||
AllowedPatterns: []string{"*.jpg"},
|
||||
},
|
||||
{
|
||||
Path: "/dir3",
|
||||
DenyPolicy: sdk.DenyPolicyDefault,
|
||||
DeniedPatterns: []string{"*.jpg"},
|
||||
},
|
||||
{
|
||||
Path: "/dir4",
|
||||
DenyPolicy: sdk.DenyPolicyHide,
|
||||
DeniedPatterns: []string{"*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -693,13 +704,90 @@ func TestFilterListDirs(t *testing.T) {
|
|||
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
|
||||
vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
|
||||
}
|
||||
|
||||
// dirContents are modified in place, we need to redefine them each time
|
||||
filtered := user.FilterListDir(dirContents, "/dir1")
|
||||
assert.Len(t, filtered, 5)
|
||||
|
||||
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/vdir1")
|
||||
assert.Len(t, filtered, 2)
|
||||
|
||||
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, "/dir2/vdir2")
|
||||
require.Len(t, filtered, 1)
|
||||
assert.Equal(t, "file1.jpg", filtered[0].Name())
|
||||
|
||||
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, "/dir2/vdir2/sub")
|
||||
require.Len(t, filtered, 1)
|
||||
assert.Equal(t, "file1.jpg", filtered[0].Name())
|
||||
|
||||
res, _ := user.IsFileAllowed("/dir1/vdir1/file.txt")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir1/vdir1/sub/file.txt")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir1/vdir1/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir1/vdir1/sub/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/dir1/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/dir1/sub/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir4/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir4/dir1/sub/file.jpg")
|
||||
assert.False(t, res)
|
||||
|
||||
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, "/dir4")
|
||||
require.Len(t, filtered, 0)
|
||||
|
||||
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, "/dir4/vdir2/sub")
|
||||
require.Len(t, filtered, 0)
|
||||
|
||||
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, "/dir2")
|
||||
assert.Len(t, filtered, 2)
|
||||
|
||||
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, "/dir4")
|
||||
assert.Len(t, filtered, 0)
|
||||
|
||||
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, "/dir4/sub")
|
||||
assert.Len(t, filtered, 0)
|
||||
|
||||
dirContents = []os.FileInfo{
|
||||
vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
|
||||
vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
|
||||
|
@ -708,11 +796,6 @@ func TestFilterListDirs(t *testing.T) {
|
|||
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())
|
||||
|
@ -799,7 +882,155 @@ func TestFilterListDirs(t *testing.T) {
|
|||
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())
|
||||
require.Len(t, filtered, 1)
|
||||
assert.Equal(t, "ic35", filtered[0].Name())
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic36")
|
||||
require.Len(t, filtered, 0)
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35")
|
||||
require.Len(t, filtered, 3)
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35/sub")
|
||||
require.Len(t, filtered, 3)
|
||||
|
||||
res, _ = user.IsFileAllowed("/dir3/file.txt")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35a")
|
||||
assert.False(t, res)
|
||||
res, policy := user.IsFileAllowed("/dir3/ic35a/file")
|
||||
assert.False(t, res)
|
||||
assert.Equal(t, sdk.DenyPolicyHide, policy)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub/file.txt")
|
||||
assert.True(t, res)
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35/sub")
|
||||
require.Len(t, filtered, 3)
|
||||
|
||||
user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
|
||||
Path: "/dir3/ic35/sub1",
|
||||
AllowedPatterns: []string{"*.jpg"},
|
||||
DenyPolicy: sdk.DenyPolicyDefault,
|
||||
})
|
||||
user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
|
||||
Path: "/dir3/ic35/sub2",
|
||||
DeniedPatterns: []string{"*.jpg"},
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35/sub1")
|
||||
require.Len(t, filtered, 3)
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35/sub2")
|
||||
require.Len(t, filtered, 2)
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35/sub2/sub1")
|
||||
require.Len(t, filtered, 2)
|
||||
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub/dir/file.txt")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub/dir/file.jpg")
|
||||
assert.True(t, res)
|
||||
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub1/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub1/file.txt")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub1/sub/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub1/sub2/file.txt")
|
||||
assert.False(t, res)
|
||||
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.txt")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub1/file.txt")
|
||||
assert.True(t, res)
|
||||
|
||||
user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
|
||||
Path: "/dir3/ic35",
|
||||
DeniedPatterns: []string{"*.txt"},
|
||||
DenyPolicy: sdk.DenyPolicyHide,
|
||||
})
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/adir/sub/file.jpg")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/adir/file.txt")
|
||||
assert.False(t, res)
|
||||
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.txt")
|
||||
assert.True(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub/file.jpg")
|
||||
assert.False(t, res)
|
||||
res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub1/file.txt")
|
||||
assert.True(t, res)
|
||||
|
||||
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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35")
|
||||
require.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),
|
||||
}
|
||||
filtered = user.FilterListDir(dirContents, "/dir3/ic35/abc")
|
||||
require.Len(t, filtered, 1)
|
||||
}
|
||||
|
|
|
@ -981,9 +981,14 @@ func (u *User) getPatternsFilterForPath(virtualPath string) sdk.PatternsFilter {
|
|||
return filter
|
||||
}
|
||||
dirsForPath := util.GetDirsForVirtualPath(virtualPath)
|
||||
for _, dir := range dirsForPath {
|
||||
for idx, dir := range dirsForPath {
|
||||
for _, f := range u.Filters.FilePatterns {
|
||||
if f.Path == dir {
|
||||
if idx > 0 && len(f.AllowedPatterns) > 0 && len(f.DeniedPatterns) > 0 && f.DeniedPatterns[0] == "*" {
|
||||
if f.CheckAllowed(path.Base(dirsForPath[idx-1])) {
|
||||
return filter
|
||||
}
|
||||
}
|
||||
filter = f
|
||||
break
|
||||
}
|
||||
|
@ -1004,7 +1009,7 @@ func (u *User) isDirHidden(virtualPath string) bool {
|
|||
return false
|
||||
}
|
||||
filter := u.getPatternsFilterForPath(dirPath)
|
||||
if filter.DenyPolicy == sdk.DenyPolicyHide {
|
||||
if filter.DenyPolicy == sdk.DenyPolicyHide && filter.Path != dirPath {
|
||||
if !filter.CheckAllowed(path.Base(dirPath)) {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-4">Comma separated denied or allowed files/directories, based on shell patterns.</h6>
|
||||
<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>
|
||||
<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. Setting a denied pattern as "*" and allowed pattern/s for the same directory you can create denied except rules, but note that if you allow a directory, everything in it will be allowed unless more specific patterns/permissions are defined.</p>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 form_field_patterns_outer">
|
||||
{{range $idx, $pattern := .Group.UserSettings.Filters.GetFlatFilePatterns -}}
|
||||
|
|
|
@ -502,7 +502,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-4">Comma separated denied or allowed files/directories, based on shell patterns.</h6>
|
||||
<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>
|
||||
<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. Setting a denied pattern as "*" and allowed pattern/s for the same directory you can create denied except rules, but note that if you allow a directory, everything in it will be allowed unless more specific patterns/permissions are defined.</p>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 form_field_patterns_outer">
|
||||
{{range $idx, $pattern := .User.Filters.GetFlatFilePatterns -}}
|
||||
|
|
Loading…
Reference in a new issue