mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 06:40:54 +03:00
add separate permissions to delete and rename files and dirs
perm_delete and perm_rename still exist for backward compatibility, now they are an alias to assign both new split permissions
This commit is contained in:
2
.github/workflows/development.yml
vendored
2
.github/workflows/development.yml
vendored
@@ -280,7 +280,7 @@ jobs:
|
|||||||
- arch: armv7
|
- arch: armv7
|
||||||
distro: ubuntu18.04
|
distro: ubuntu18.04
|
||||||
go: latest
|
go: latest
|
||||||
go-arch: arm
|
go-arch: arm7
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -213,9 +213,9 @@ jobs:
|
|||||||
tar-arch: ppc64le
|
tar-arch: ppc64le
|
||||||
- arch: armv7
|
- arch: armv7
|
||||||
distro: ubuntu18.04
|
distro: ubuntu18.04
|
||||||
go-arch: arm
|
go-arch: arm7
|
||||||
deb-arch: armhf
|
deb-arch: armhf
|
||||||
rpm-arch: arm
|
rpm-arch: armv7hl
|
||||||
tar-arch: armv7
|
tar-arch: armv7
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -822,6 +822,23 @@ func TestHideConfidentialData(t *testing.T) {
|
|||||||
a.HideConfidentialData()
|
a.HideConfidentialData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUserPerms(t *testing.T) {
|
||||||
|
u := dataprovider.User{}
|
||||||
|
u.Permissions = make(map[string][]string)
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermUpload, dataprovider.PermDelete}
|
||||||
|
assert.True(t, u.HasAnyPerm([]string{dataprovider.PermRename, dataprovider.PermDelete}, "/"))
|
||||||
|
assert.False(t, u.HasAnyPerm([]string{dataprovider.PermRename, dataprovider.PermCreateDirs}, "/"))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermDelete, dataprovider.PermCreateDirs}
|
||||||
|
assert.True(t, u.HasPermsDeleteAll("/"))
|
||||||
|
assert.False(t, u.HasPermsRenameAll("/"))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermDeleteDirs, dataprovider.PermDeleteFiles, dataprovider.PermRenameDirs}
|
||||||
|
assert.True(t, u.HasPermsDeleteAll("/"))
|
||||||
|
assert.False(t, u.HasPermsRenameAll("/"))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermDeleteDirs, dataprovider.PermRenameFiles, dataprovider.PermRenameDirs}
|
||||||
|
assert.False(t, u.HasPermsDeleteAll("/"))
|
||||||
|
assert.True(t, u.HasPermsRenameAll("/"))
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkBcryptHashing(b *testing.B) {
|
func BenchmarkBcryptHashing(b *testing.B) {
|
||||||
bcryptPassword := "bcryptpassword"
|
bcryptPassword := "bcryptpassword"
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ func (c *BaseConnection) CreateDir(virtualPath string) error {
|
|||||||
|
|
||||||
// IsRemoveFileAllowed returns an error if removing this file is not allowed
|
// IsRemoveFileAllowed returns an error if removing this file is not allowed
|
||||||
func (c *BaseConnection) IsRemoveFileAllowed(virtualPath string) error {
|
func (c *BaseConnection) IsRemoveFileAllowed(virtualPath string) error {
|
||||||
if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(virtualPath)) {
|
if !c.User.HasAnyPerm([]string{dataprovider.PermDeleteFiles, dataprovider.PermDelete}, path.Dir(virtualPath)) {
|
||||||
return c.GetPermissionDeniedError()
|
return c.GetPermissionDeniedError()
|
||||||
}
|
}
|
||||||
if !c.User.IsFileAllowed(virtualPath) {
|
if !c.User.IsFileAllowed(virtualPath) {
|
||||||
@@ -323,7 +323,7 @@ func (c *BaseConnection) IsRemoveDirAllowed(fs vfs.Fs, fsPath, virtualPath strin
|
|||||||
c.Log(logger.LevelWarn, "removing a directory mapped as virtual folder is not allowed: %#v", fsPath)
|
c.Log(logger.LevelWarn, "removing a directory mapped as virtual folder is not allowed: %#v", fsPath)
|
||||||
return c.GetPermissionDeniedError()
|
return c.GetPermissionDeniedError()
|
||||||
}
|
}
|
||||||
if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(virtualPath)) {
|
if !c.User.HasAnyPerm([]string{dataprovider.PermDeleteDirs, dataprovider.PermDelete}, path.Dir(virtualPath)) {
|
||||||
return c.GetPermissionDeniedError()
|
return c.GetPermissionDeniedError()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -650,11 +650,11 @@ func (c *BaseConnection) checkRecursiveRenameDirPermissions(fsSrc, fsDst vfs.Fs,
|
|||||||
// inside the parent path was checked. If the current dir has no subdirs with defined permissions inside it
|
// inside the parent path was checked. If the current dir has no subdirs with defined permissions inside it
|
||||||
// and it has all the possible permissions we can stop scanning
|
// and it has all the possible permissions we can stop scanning
|
||||||
if !c.User.HasPermissionsInside(path.Dir(virtualSrcPath)) && !c.User.HasPermissionsInside(path.Dir(virtualDstPath)) {
|
if !c.User.HasPermissionsInside(path.Dir(virtualSrcPath)) && !c.User.HasPermissionsInside(path.Dir(virtualDstPath)) {
|
||||||
if c.User.HasPerm(dataprovider.PermRename, path.Dir(virtualSrcPath)) &&
|
if c.User.HasPermsRenameAll(path.Dir(virtualSrcPath)) &&
|
||||||
c.User.HasPerm(dataprovider.PermRename, path.Dir(virtualDstPath)) {
|
c.User.HasPermsRenameAll(path.Dir(virtualDstPath)) {
|
||||||
return ErrSkipPermissionsCheck
|
return ErrSkipPermissionsCheck
|
||||||
}
|
}
|
||||||
if c.User.HasPerm(dataprovider.PermDelete, path.Dir(virtualSrcPath)) &&
|
if c.User.HasPermsDeleteAll(path.Dir(virtualSrcPath)) &&
|
||||||
c.User.HasPerms(dstPerms, path.Dir(virtualDstPath)) {
|
c.User.HasPerms(dstPerms, path.Dir(virtualDstPath)) {
|
||||||
return ErrSkipPermissionsCheck
|
return ErrSkipPermissionsCheck
|
||||||
}
|
}
|
||||||
@@ -673,19 +673,42 @@ func (c *BaseConnection) checkRecursiveRenameDirPermissions(fsSrc, fsDst vfs.Fs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *BaseConnection) hasRenamePerms(virtualSourcePath, virtualTargetPath string, fi os.FileInfo) bool {
|
func (c *BaseConnection) hasRenamePerms(virtualSourcePath, virtualTargetPath string, fi os.FileInfo) bool {
|
||||||
if c.User.HasPerm(dataprovider.PermRename, path.Dir(virtualSourcePath)) &&
|
if c.User.HasPermsRenameAll(path.Dir(virtualSourcePath)) &&
|
||||||
c.User.HasPerm(dataprovider.PermRename, path.Dir(virtualTargetPath)) {
|
c.User.HasPermsRenameAll(path.Dir(virtualTargetPath)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(virtualSourcePath)) {
|
if fi == nil {
|
||||||
|
// we don't know if this is a file or a directory we have to check all the required permissions
|
||||||
|
if !c.User.HasPermsDeleteAll(path.Dir(virtualSourcePath)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if fi != nil {
|
dstPerms := []string{
|
||||||
if fi.IsDir() {
|
dataprovider.PermCreateDirs,
|
||||||
return c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(virtualTargetPath))
|
dataprovider.PermUpload,
|
||||||
} else if fi.Mode()&os.ModeSymlink != 0 {
|
dataprovider.PermCreateSymlinks,
|
||||||
return c.User.HasPerm(dataprovider.PermCreateSymlinks, path.Dir(virtualTargetPath))
|
|
||||||
}
|
}
|
||||||
|
return c.User.HasPerms(dstPerms, path.Dir(virtualTargetPath))
|
||||||
|
}
|
||||||
|
if fi.IsDir() {
|
||||||
|
if c.User.HasPerm(dataprovider.PermRenameDirs, path.Dir(virtualSourcePath)) &&
|
||||||
|
c.User.HasPerm(dataprovider.PermRenameDirs, path.Dir(virtualTargetPath)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !c.User.HasAnyPerm([]string{dataprovider.PermDeleteDirs, dataprovider.PermDelete}, path.Dir(virtualSourcePath)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(virtualTargetPath))
|
||||||
|
}
|
||||||
|
// file or symlink
|
||||||
|
if c.User.HasPerm(dataprovider.PermRenameFiles, path.Dir(virtualSourcePath)) &&
|
||||||
|
c.User.HasPerm(dataprovider.PermRenameFiles, path.Dir(virtualTargetPath)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !c.User.HasAnyPerm([]string{dataprovider.PermDeleteFiles, dataprovider.PermDelete}, path.Dir(virtualSourcePath)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return c.User.HasPerm(dataprovider.PermCreateSymlinks, path.Dir(virtualTargetPath))
|
||||||
}
|
}
|
||||||
return c.User.HasPerm(dataprovider.PermUpload, path.Dir(virtualTargetPath))
|
return c.User.HasPerm(dataprovider.PermUpload, path.Dir(virtualTargetPath))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,6 +148,31 @@ func TestRenameVirtualFolders(t *testing.T) {
|
|||||||
assert.False(t, res)
|
assert.False(t, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenamePerms(t *testing.T) {
|
||||||
|
src := "source"
|
||||||
|
target := "target"
|
||||||
|
u := dataprovider.User{}
|
||||||
|
u.Permissions = map[string][]string{}
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
|
||||||
|
dataprovider.PermDeleteFiles}
|
||||||
|
conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
|
||||||
|
assert.False(t, conn.hasRenamePerms(src, target, nil))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
|
||||||
|
dataprovider.PermDeleteFiles, dataprovider.PermDeleteDirs}
|
||||||
|
assert.True(t, conn.hasRenamePerms(src, target, nil))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles,
|
||||||
|
dataprovider.PermDeleteDirs}
|
||||||
|
assert.False(t, conn.hasRenamePerms(src, target, nil))
|
||||||
|
|
||||||
|
info := vfs.NewFileInfo(src, true, 0, time.Now(), false)
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles}
|
||||||
|
assert.False(t, conn.hasRenamePerms(src, target, info))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
|
||||||
|
assert.True(t, conn.hasRenamePerms(src, target, info))
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
|
||||||
|
assert.False(t, conn.hasRenamePerms(src, target, info))
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdateQuotaAfterRename(t *testing.T) {
|
func TestUpdateQuotaAfterRename(t *testing.T) {
|
||||||
user := dataprovider.User{
|
user := dataprovider.User{
|
||||||
BaseUser: sdk.BaseUser{
|
BaseUser: sdk.BaseUser{
|
||||||
|
|||||||
@@ -236,13 +236,13 @@ func (c *RetentionCheck) removeFile(virtualPath string, info os.FileInfo) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RetentionCheck) cleanupFolder(folderPath string) error {
|
func (c *RetentionCheck) cleanupFolder(folderPath string) error {
|
||||||
cleanupPerms := []string{dataprovider.PermListItems, dataprovider.PermDelete}
|
deleteFilesPerms := []string{dataprovider.PermDelete, dataprovider.PermDeleteFiles}
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
result := &folderRetentionCheckResult{
|
result := &folderRetentionCheckResult{
|
||||||
Path: folderPath,
|
Path: folderPath,
|
||||||
}
|
}
|
||||||
c.results = append(c.results, result)
|
c.results = append(c.results, result)
|
||||||
if !c.conn.User.HasPerms(cleanupPerms, folderPath) {
|
if !c.conn.User.HasPerm(dataprovider.PermListItems, folderPath) || !c.conn.User.HasAnyPerm(deleteFilesPerms, folderPath) {
|
||||||
result.Elapsed = time.Since(startTime)
|
result.Elapsed = time.Since(startTime)
|
||||||
result.Info = "data retention check skipped: no permissions"
|
result.Info = "data retention check skipped: no permissions"
|
||||||
c.conn.Log(logger.LevelInfo, "user %#v does not have permissions to check retention on %#v, retention check skipped",
|
c.conn.Log(logger.LevelInfo, "user %#v does not have permissions to check retention on %#v, retention check skipped",
|
||||||
@@ -316,7 +316,11 @@ func (c *RetentionCheck) cleanupFolder(folderPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RetentionCheck) checkEmptyDirRemoval(folderPath string) {
|
func (c *RetentionCheck) checkEmptyDirRemoval(folderPath string) {
|
||||||
if folderPath != "/" && c.conn.User.HasPerm(dataprovider.PermDelete, path.Dir(folderPath)) {
|
if folderPath != "/" && c.conn.User.HasAnyPerm([]string{
|
||||||
|
dataprovider.PermDelete,
|
||||||
|
dataprovider.PermDeleteDirs,
|
||||||
|
}, path.Dir(folderPath),
|
||||||
|
) {
|
||||||
files, err := c.conn.ListDir(folderPath)
|
files, err := c.conn.ListDir(folderPath)
|
||||||
if err == nil && len(files) == 0 {
|
if err == nil && len(files) == 0 {
|
||||||
err = c.conn.RemoveDir(folderPath)
|
err = c.conn.RemoveDir(folderPath)
|
||||||
|
|||||||
@@ -2668,6 +2668,92 @@ func TestRenameSymlink(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSplittedDeletePerms(t *testing.T) {
|
||||||
|
u := getTestUser()
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDeleteDirs,
|
||||||
|
dataprovider.PermCreateDirs}
|
||||||
|
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
conn, client, err := getSftpClient(user)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
defer conn.Close()
|
||||||
|
defer client.Close()
|
||||||
|
err = writeSFTPFile(testFileName, 4096, client)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Remove(testFileName)
|
||||||
|
assert.Error(t, err)
|
||||||
|
err = client.Mkdir(testDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.RemoveDirectory(testDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDeleteFiles,
|
||||||
|
dataprovider.PermCreateDirs, dataprovider.PermOverwrite}
|
||||||
|
_, _, err = httpdtest.UpdateUser(u, http.StatusOK, "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
conn, client, err = getSftpClient(user)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
defer conn.Close()
|
||||||
|
defer client.Close()
|
||||||
|
err = writeSFTPFile(testFileName, 4096, client)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Remove(testFileName)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Mkdir(testDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.RemoveDirectory(testDir)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = os.RemoveAll(user.GetHomeDir())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSplittedRenamePerms(t *testing.T) {
|
||||||
|
u := getTestUser()
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermRenameDirs,
|
||||||
|
dataprovider.PermCreateDirs}
|
||||||
|
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
conn, client, err := getSftpClient(user)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
defer conn.Close()
|
||||||
|
defer client.Close()
|
||||||
|
err = writeSFTPFile(testFileName, 4096, client)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Mkdir(testDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Rename(testFileName, testFileName+"_renamed")
|
||||||
|
assert.Error(t, err)
|
||||||
|
err = client.Rename(testDir, testDir+"_renamed")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermRenameFiles,
|
||||||
|
dataprovider.PermCreateDirs, dataprovider.PermOverwrite}
|
||||||
|
_, _, err = httpdtest.UpdateUser(u, http.StatusOK, "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
conn, client, err = getSftpClient(user)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
defer conn.Close()
|
||||||
|
defer client.Close()
|
||||||
|
err = writeSFTPFile(testFileName, 4096, client)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Mkdir(testDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Rename(testFileName, testFileName+"_renamed")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = client.Rename(testDir, testDir+"_renamed")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = os.RemoveAll(user.GetHomeDir())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSFTPLoopError(t *testing.T) {
|
func TestSFTPLoopError(t *testing.T) {
|
||||||
user1 := getTestUser()
|
user1 := getTestUser()
|
||||||
user2 := getTestUser()
|
user2 := getTestUser()
|
||||||
|
|||||||
@@ -114,8 +114,9 @@ var (
|
|||||||
SupportedProviders = []string{SQLiteDataProviderName, PGSQLDataProviderName, MySQLDataProviderName,
|
SupportedProviders = []string{SQLiteDataProviderName, PGSQLDataProviderName, MySQLDataProviderName,
|
||||||
BoltDataProviderName, MemoryDataProviderName, CockroachDataProviderName}
|
BoltDataProviderName, MemoryDataProviderName, CockroachDataProviderName}
|
||||||
// ValidPerms defines all the valid permissions for a user
|
// ValidPerms defines all the valid permissions for a user
|
||||||
ValidPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermOverwrite, PermRename, PermDelete,
|
ValidPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermOverwrite, PermCreateDirs, PermRename,
|
||||||
PermCreateDirs, PermCreateSymlinks, PermChmod, PermChown, PermChtimes}
|
PermRenameFiles, PermRenameDirs, PermDelete, PermDeleteFiles, PermDeleteDirs, PermCreateSymlinks, PermChmod,
|
||||||
|
PermChown, PermChtimes}
|
||||||
// ValidLoginMethods defines all the valid login methods
|
// ValidLoginMethods defines all the valid login methods
|
||||||
ValidLoginMethods = []string{SSHLoginMethodPublicKey, LoginMethodPassword, SSHLoginMethodKeyboardInteractive,
|
ValidLoginMethods = []string{SSHLoginMethodPublicKey, LoginMethodPassword, SSHLoginMethodKeyboardInteractive,
|
||||||
SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt, LoginMethodTLSCertificate,
|
SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt, LoginMethodTLSCertificate,
|
||||||
|
|||||||
@@ -38,8 +38,16 @@ const (
|
|||||||
PermOverwrite = "overwrite"
|
PermOverwrite = "overwrite"
|
||||||
// delete files or directories is allowed
|
// delete files or directories is allowed
|
||||||
PermDelete = "delete"
|
PermDelete = "delete"
|
||||||
|
// delete files is allowed
|
||||||
|
PermDeleteFiles = "delete_files"
|
||||||
|
// delete directories is allowed
|
||||||
|
PermDeleteDirs = "delete_dirs"
|
||||||
// rename files or directories is allowed
|
// rename files or directories is allowed
|
||||||
PermRename = "rename"
|
PermRename = "rename"
|
||||||
|
// rename files is allowed
|
||||||
|
PermRenameFiles = "rename_files"
|
||||||
|
// rename directories is allowed
|
||||||
|
PermRenameDirs = "rename_dirs"
|
||||||
// create directories is allowed
|
// create directories is allowed
|
||||||
PermCreateDirs = "create_dirs"
|
PermCreateDirs = "create_dirs"
|
||||||
// create symbolic links is allowed
|
// create symbolic links is allowed
|
||||||
@@ -66,6 +74,9 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
errNoMatchingVirtualFolder = errors.New("no matching virtual folder found")
|
errNoMatchingVirtualFolder = errors.New("no matching virtual folder found")
|
||||||
|
permsRenameAny = []string{PermRename, PermRenameDirs, PermRenameFiles}
|
||||||
|
permsDeleteAny = []string{PermDelete, PermDeleteDirs, PermDeleteFiles}
|
||||||
|
permsCreateAny = []string{PermUpload, PermCreateDirs}
|
||||||
)
|
)
|
||||||
|
|
||||||
// User defines a SFTPGo user
|
// User defines a SFTPGo user
|
||||||
@@ -564,7 +575,21 @@ func (u *User) HasPerm(permission, path string) bool {
|
|||||||
return util.IsStringInSlice(permission, perms)
|
return util.IsStringInSlice(permission, perms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPerms return true if the user has all the given permissions
|
// HasAnyPerm returns true if the user has at least one of the given permissions
|
||||||
|
func (u *User) HasAnyPerm(permissions []string, path string) bool {
|
||||||
|
perms := u.GetPermissionsForPath(path)
|
||||||
|
if util.IsStringInSlice(PermAny, perms) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, permission := range permissions {
|
||||||
|
if util.IsStringInSlice(permission, perms) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPerms returns true if the user has all the given permissions
|
||||||
func (u *User) HasPerms(permissions []string, path string) bool {
|
func (u *User) HasPerms(permissions []string, path string) bool {
|
||||||
perms := u.GetPermissionsForPath(path)
|
perms := u.GetPermissionsForPath(path)
|
||||||
if util.IsStringInSlice(PermAny, perms) {
|
if util.IsStringInSlice(PermAny, perms) {
|
||||||
@@ -578,6 +603,46 @@ func (u *User) HasPerms(permissions []string, path string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasPermsDeleteAll returns true if the user can delete both files and directories
|
||||||
|
// for the given path
|
||||||
|
func (u *User) HasPermsDeleteAll(path string) bool {
|
||||||
|
perms := u.GetPermissionsForPath(path)
|
||||||
|
canDeleteFiles := false
|
||||||
|
canDeleteDirs := false
|
||||||
|
for _, permission := range perms {
|
||||||
|
if permission == PermAny || permission == PermDelete {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if permission == PermDeleteFiles {
|
||||||
|
canDeleteFiles = true
|
||||||
|
}
|
||||||
|
if permission == PermDeleteDirs {
|
||||||
|
canDeleteDirs = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canDeleteFiles && canDeleteDirs
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPermsRenameAll returns true if the user can rename both files and directories
|
||||||
|
// for the given path
|
||||||
|
func (u *User) HasPermsRenameAll(path string) bool {
|
||||||
|
perms := u.GetPermissionsForPath(path)
|
||||||
|
canRenameFiles := false
|
||||||
|
canRenameDirs := false
|
||||||
|
for _, permission := range perms {
|
||||||
|
if permission == PermAny || permission == PermRename {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if permission == PermRenameFiles {
|
||||||
|
canRenameFiles = true
|
||||||
|
}
|
||||||
|
if permission == PermRenameDirs {
|
||||||
|
canRenameDirs = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canRenameFiles && canRenameDirs
|
||||||
|
}
|
||||||
|
|
||||||
// HasNoQuotaRestrictions returns true if no quota restrictions need to be applyed
|
// HasNoQuotaRestrictions returns true if no quota restrictions need to be applyed
|
||||||
func (u *User) HasNoQuotaRestrictions(checkFiles bool) bool {
|
func (u *User) HasNoQuotaRestrictions(checkFiles bool) bool {
|
||||||
if u.QuotaSize == 0 && (!checkFiles || u.QuotaFiles == 0) {
|
if u.QuotaSize == 0 && (!checkFiles || u.QuotaFiles == 0) {
|
||||||
@@ -782,13 +847,13 @@ func (u *User) CanRenameFromWeb(src, dest string) bool {
|
|||||||
if util.IsStringInSlice(sdk.WebClientWriteDisabled, u.Filters.WebClient) {
|
if util.IsStringInSlice(sdk.WebClientWriteDisabled, u.Filters.WebClient) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if u.HasPerm(PermRename, src) && u.HasPerm(PermRename, dest) {
|
if u.HasAnyPerm(permsRenameAny, src) && u.HasAnyPerm(permsRenameAny, dest) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !u.HasPerm(PermDelete, src) {
|
if !u.HasAnyPerm(permsDeleteAny, src) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return u.HasPerm(PermUpload, dest) || u.HasPerm(PermCreateDirs, dest)
|
return u.HasAnyPerm(permsCreateAny, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDeleteFromWeb returns true if the client can delete objects from the web UI.
|
// CanDeleteFromWeb returns true if the client can delete objects from the web UI.
|
||||||
@@ -797,7 +862,7 @@ func (u *User) CanDeleteFromWeb(target string) bool {
|
|||||||
if util.IsStringInSlice(sdk.WebClientWriteDisabled, u.Filters.WebClient) {
|
if util.IsStringInSlice(sdk.WebClientWriteDisabled, u.Filters.WebClient) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return u.HasPerm(PermDelete, target)
|
return u.HasAnyPerm(permsDeleteAny, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSignature returns a signature for this admin.
|
// GetSignature returns a signature for this admin.
|
||||||
|
|||||||
@@ -3424,7 +3424,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- user APIs
|
- user APIs
|
||||||
summary: Rename a directory
|
summary: Rename a directory
|
||||||
description: Rename a directory for the logged in user. The rename is allowed for empty directory or for non empty, local directories, with no virtual folders inside
|
description: Rename a directory for the logged in user. The rename is allowed for empty directory or for non empty local directories, with no virtual folders inside
|
||||||
operationId: rename_user_dir
|
operationId: rename_user_dir
|
||||||
parameters:
|
parameters:
|
||||||
- in: query
|
- in: query
|
||||||
@@ -3772,7 +3772,11 @@ components:
|
|||||||
- upload
|
- upload
|
||||||
- overwrite
|
- overwrite
|
||||||
- delete
|
- delete
|
||||||
|
- delete_files
|
||||||
|
- delete_dirs
|
||||||
- rename
|
- rename
|
||||||
|
- rename_files
|
||||||
|
- rename_dirs
|
||||||
- create_dirs
|
- create_dirs
|
||||||
- create_symlinks
|
- create_symlinks
|
||||||
- chmod
|
- chmod
|
||||||
@@ -3786,7 +3790,11 @@ components:
|
|||||||
* `upload` - upload files is allowed
|
* `upload` - upload files is allowed
|
||||||
* `overwrite` - overwrite an existing file, while uploading, is allowed. upload permission is required to allow file overwrite
|
* `overwrite` - overwrite an existing file, while uploading, is allowed. upload permission is required to allow file overwrite
|
||||||
* `delete` - delete files or directories is allowed
|
* `delete` - delete files or directories is allowed
|
||||||
|
* `delete_files` - delete files is allowed
|
||||||
|
* `delete_dirs` - delete directories is allowed
|
||||||
* `rename` - rename files or directories is allowed
|
* `rename` - rename files or directories is allowed
|
||||||
|
* `rename_files` - rename files is allowed
|
||||||
|
* `rename_dirs` - rename directories is allowed
|
||||||
* `create_dirs` - create directories is allowed
|
* `create_dirs` - create directories is allowed
|
||||||
* `create_symlinks` - create links is allowed
|
* `create_symlinks` - create links is allowed
|
||||||
* `chmod` changing file or directory permissions is allowed
|
* `chmod` changing file or directory permissions is allowed
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
NFPM_VERSION=2.8.0
|
NFPM_VERSION=2.9.2
|
||||||
NFPM_ARCH=${NFPM_ARCH:-amd64}
|
NFPM_ARCH=${NFPM_ARCH:-amd64}
|
||||||
if [ -z ${SFTPGO_VERSION} ]
|
if [ -z ${SFTPGO_VERSION} ]
|
||||||
then
|
then
|
||||||
LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
|
LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
|
||||||
NUM_COMMITS_FROM_TAG=$(git rev-list ${LATEST_TAG}.. --count)
|
NUM_COMMITS_FROM_TAG=$(git rev-list ${LATEST_TAG}.. --count)
|
||||||
#COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
||||||
VERSION=$(echo "${LATEST_TAG}" | awk -F. -v OFS=. '{$NF++;print}')-dev.${NUM_COMMITS_FROM_TAG}
|
VERSION=$(echo "${LATEST_TAG}" | awk -F. -v OFS=. '{$NF++;print}')-dev.${NUM_COMMITS_FROM_TAG}
|
||||||
else
|
else
|
||||||
VERSION=${SFTPGO_VERSION}
|
VERSION=${SFTPGO_VERSION}
|
||||||
@@ -87,10 +86,11 @@ contents:
|
|||||||
dst: "/etc/sftpgo/sftpgo.json"
|
dst: "/etc/sftpgo/sftpgo.json"
|
||||||
type: "config|noreplace"
|
type: "config|noreplace"
|
||||||
|
|
||||||
|
- dst: "/srv/sftpgo"
|
||||||
|
type: dir
|
||||||
|
|
||||||
empty_folders:
|
- dst: "/var/lib/sftpgo"
|
||||||
- /var/lib/sftpgo
|
type: dir
|
||||||
- /srv/sftpgo
|
|
||||||
|
|
||||||
overrides:
|
overrides:
|
||||||
deb:
|
deb:
|
||||||
|
|||||||
Reference in New Issue
Block a user