mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-08 23:28:39 +03:00
virtual folders: allow overlapped mapped paths if quota is disabled
See #95
This commit is contained in:
@@ -304,6 +304,14 @@ func (c Connection) handleSFTPRename(sourcePath, targetPath string, request *sft
|
||||
if !c.isRenamePermitted(sourcePath, request) {
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
if c.User.IsMappedPath(sourcePath) {
|
||||
c.Log(logger.LevelWarn, logSender, "renaming a directory mapped as virtual folder is not allowed: %#v", sourcePath)
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
if c.User.IsMappedPath(targetPath) {
|
||||
c.Log(logger.LevelWarn, logSender, "renaming to a directory mapped as virtual folder is not allowed: %#v", targetPath)
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
if c.User.HasVirtualFoldersInside(request.Filepath) {
|
||||
if fi, err := c.fs.Stat(sourcePath); err == nil {
|
||||
if fi.IsDir() {
|
||||
@@ -359,6 +367,10 @@ func (c Connection) handleSFTPRmdir(dirPath string, request *sftp.Request) error
|
||||
c.Log(logger.LevelWarn, logSender, "removing a directory with a virtual folder inside is not allowed: %#v", request.Filepath)
|
||||
return sftp.ErrSSHFxOpUnsupported
|
||||
}
|
||||
if c.User.IsMappedPath(dirPath) {
|
||||
c.Log(logger.LevelWarn, logSender, "removing a directory mapped as virtual folder is not allowed: %#v", dirPath)
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(request.Filepath)) {
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
@@ -399,6 +411,14 @@ func (c Connection) handleSFTPSymlink(sourcePath string, targetPath string, requ
|
||||
c.Log(logger.LevelWarn, logSender, "cross folder symlink is not supported, src: %v dst: %v", request.Filepath, request.Target)
|
||||
return sftp.ErrSSHFxFailure
|
||||
}
|
||||
if c.User.IsMappedPath(sourcePath) {
|
||||
c.Log(logger.LevelWarn, logSender, "symlinking a directory mapped as virtual folder is not allowed: %#v", sourcePath)
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
if c.User.IsMappedPath(targetPath) {
|
||||
c.Log(logger.LevelWarn, logSender, "symlinking to a directory mapped as virtual folder is not allowed: %#v", targetPath)
|
||||
return sftp.ErrSSHFxPermissionDenied
|
||||
}
|
||||
if err := c.fs.Symlink(sourcePath, targetPath); err != nil {
|
||||
c.Log(logger.LevelWarn, logSender, "failed to create symlink %#v -> %#v: %+v", sourcePath, targetPath, err)
|
||||
return vfs.GetSFTPError(c.fs, err)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -475,6 +476,11 @@ func loginUser(user dataprovider.User, loginMethod, publicKey string, conn ssh.C
|
||||
logger.Debug(logSender, connectionID, "cannot login user %#v, login method %#v is not allowed", user.Username, loginMethod)
|
||||
return nil, fmt.Errorf("Login method %#v is not allowed for user %#v", loginMethod, user.Username)
|
||||
}
|
||||
if dataprovider.GetQuotaTracking() > 0 && user.HasOverlappedMappedPaths() {
|
||||
logger.Debug(logSender, connectionID, "cannot login user %#v, overlapping mapped folders are allowed only with quota tracking disabled",
|
||||
user.Username)
|
||||
return nil, errors.New("overlapping mapped folders are allowed only with quota tracking disabled")
|
||||
}
|
||||
remoteAddr := conn.RemoteAddr().String()
|
||||
if !user.IsLoginFromAddrAllowed(remoteAddr) {
|
||||
logger.Debug(logSender, connectionID, "cannot login user %#v, remote address is not allowed: %v", user.Username, remoteAddr)
|
||||
|
||||
@@ -3547,6 +3547,124 @@ func TestVirtualFoldersLink(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestOverlappedMappedFolders(t *testing.T) {
|
||||
dataProvider := dataprovider.GetProvider()
|
||||
err := dataprovider.Close(dataProvider)
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf := config.GetProviderConf()
|
||||
providerConf.TrackQuota = 0
|
||||
err = dataprovider.Initialize(providerConf, configDir)
|
||||
assert.NoError(t, err)
|
||||
httpd.SetDataProvider(dataprovider.GetProvider())
|
||||
sftpd.SetDataProvider(dataprovider.GetProvider())
|
||||
|
||||
usePubKey := false
|
||||
u := getTestUser(usePubKey)
|
||||
subDir := "subdir"
|
||||
mappedPath1 := filepath.Join(os.TempDir(), "vdir1")
|
||||
vdirPath1 := "/vdir1"
|
||||
mappedPath2 := filepath.Join(os.TempDir(), "vdir1", subDir)
|
||||
vdirPath2 := "/vdir2"
|
||||
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
|
||||
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
||||
MappedPath: mappedPath1,
|
||||
},
|
||||
VirtualPath: vdirPath1,
|
||||
})
|
||||
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
|
||||
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
||||
MappedPath: mappedPath2,
|
||||
},
|
||||
VirtualPath: vdirPath2,
|
||||
})
|
||||
err = os.MkdirAll(mappedPath1, os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
err = os.MkdirAll(mappedPath2, os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
user, _, err := httpd.AddUser(u, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
client, err := getSftpClient(user, usePubKey)
|
||||
if assert.NoError(t, err) {
|
||||
defer client.Close()
|
||||
err = checkBasicSFTP(client)
|
||||
assert.NoError(t, err)
|
||||
testFileName := "test_file.dat"
|
||||
testFileSize := int64(131072)
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
err = createTestFile(testFilePath, testFileSize)
|
||||
assert.NoError(t, err)
|
||||
err = sftpUploadFile(testFilePath, testFileName, testFileSize, client)
|
||||
assert.NoError(t, err)
|
||||
err = sftpUploadFile(testFilePath, path.Join(vdirPath1, testFileName), testFileSize, client)
|
||||
assert.NoError(t, err)
|
||||
err = sftpUploadFile(testFilePath, path.Join(vdirPath2, testFileName), testFileSize, client)
|
||||
assert.NoError(t, err)
|
||||
fi, err := client.Stat(path.Join(vdirPath1, subDir, testFileName))
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, testFileSize, fi.Size())
|
||||
}
|
||||
err = client.Rename(path.Join(vdirPath1, subDir, testFileName), path.Join(vdirPath2, testFileName+"1"))
|
||||
assert.NoError(t, err)
|
||||
err = client.Rename(path.Join(vdirPath2, testFileName+"1"), path.Join(vdirPath1, subDir, testFileName))
|
||||
assert.NoError(t, err)
|
||||
err = client.Rename(path.Join(vdirPath1, subDir), path.Join(vdirPath2, subDir))
|
||||
assert.Error(t, err)
|
||||
err = client.Mkdir(subDir)
|
||||
assert.NoError(t, err)
|
||||
err = client.Rename(subDir, path.Join(vdirPath1, subDir))
|
||||
assert.Error(t, err)
|
||||
err = client.RemoveDirectory(path.Join(vdirPath1, subDir))
|
||||
assert.Error(t, err)
|
||||
err = client.Symlink(path.Join(vdirPath1, subDir), path.Join(vdirPath1, "adir"))
|
||||
assert.Error(t, err)
|
||||
err = client.Mkdir(path.Join(vdirPath1, subDir+"1"))
|
||||
assert.NoError(t, err)
|
||||
err = client.Symlink(path.Join(vdirPath1, subDir+"1"), path.Join(vdirPath1, subDir))
|
||||
assert.Error(t, err)
|
||||
err = os.Remove(testFilePath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
dataProvider = dataprovider.GetProvider()
|
||||
err = dataprovider.Close(dataProvider)
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf = config.GetProviderConf()
|
||||
err = dataprovider.Initialize(providerConf, configDir)
|
||||
assert.NoError(t, err)
|
||||
httpd.SetDataProvider(dataprovider.GetProvider())
|
||||
sftpd.SetDataProvider(dataprovider.GetProvider())
|
||||
|
||||
if providerConf.Driver != dataprovider.MemoryDataProviderName {
|
||||
client, err = getSftpClient(user, usePubKey)
|
||||
if !assert.Error(t, err) {
|
||||
client.Close()
|
||||
}
|
||||
|
||||
_, err = httpd.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath1}, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath2}, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
_, _, err = httpd.AddUser(u, http.StatusOK)
|
||||
assert.Error(t, err)
|
||||
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(mappedPath1)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(mappedPath2)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestVirtualFolderQuotaScan(t *testing.T) {
|
||||
mappedPath := filepath.Join(os.TempDir(), "mapped_dir")
|
||||
err := os.MkdirAll(mappedPath, os.ModePerm)
|
||||
|
||||
Reference in New Issue
Block a user