portable mode: add support for file extensions filters

This commit is contained in:
Nicola Murino
2020-03-05 15:37:10 +01:00
parent aaa6d0c71f
commit ec8ab28a22
4 changed files with 88 additions and 18 deletions

View File

@@ -5,7 +5,9 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings"
"github.com/drakkan/sftpgo/dataprovider" "github.com/drakkan/sftpgo/dataprovider"
"github.com/drakkan/sftpgo/service" "github.com/drakkan/sftpgo/service"
@@ -25,6 +27,8 @@ var (
portablePublicKeys []string portablePublicKeys []string
portablePermissions []string portablePermissions []string
portableSSHCommands []string portableSSHCommands []string
portableAllowedExtensions []string
portableDeniedExtensions []string
portableFsProvider int portableFsProvider int
portableS3Bucket string portableS3Bucket string
portableS3Region string portableS3Region string
@@ -113,6 +117,9 @@ Please take a look at the usage below to customize the serving parameters`,
KeyPrefix: portableGCSKeyPrefix, KeyPrefix: portableGCSKeyPrefix,
}, },
}, },
Filters: dataprovider.UserFilters{
FileExtensions: parseFileExtensionsFilters(),
},
}, },
} }
if err := service.StartPortableMode(portableSFTPDPort, portableSSHCommands, portableAdvertiseService, if err := service.StartPortableMode(portableSFTPDPort, portableSSHCommands, portableAdvertiseService,
@@ -135,6 +142,10 @@ func init() {
portableCmd.Flags().StringSliceVarP(&portablePublicKeys, "public-key", "k", []string{}, "") portableCmd.Flags().StringSliceVarP(&portablePublicKeys, "public-key", "k", []string{}, "")
portableCmd.Flags().StringSliceVarP(&portablePermissions, "permissions", "g", []string{"list", "download"}, portableCmd.Flags().StringSliceVarP(&portablePermissions, "permissions", "g", []string{"list", "download"},
"User's permissions. \"*\" means any permission") "User's permissions. \"*\" means any permission")
portableCmd.Flags().StringArrayVar(&portableAllowedExtensions, "allowed-extensions", []string{},
"Allowed file extensions case insensitive. The format is /dir::ext1,ext2. For example: \"/somedir::.jpg,.png\"")
portableCmd.Flags().StringArrayVar(&portableDeniedExtensions, "denied-extensions", []string{},
"Denied file extensions case insensitive. The format is /dir::ext1,ext2. For example: \"/somedir::.jpg,.png\"")
portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", true, portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", true,
"Advertise SFTP service using multicast DNS") "Advertise SFTP service using multicast DNS")
portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false, portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false,
@@ -158,3 +169,58 @@ func init() {
"credentials file, 1 automatic") "credentials file, 1 automatic")
rootCmd.AddCommand(portableCmd) rootCmd.AddCommand(portableCmd)
} }
func parseFileExtensionsFilters() []dataprovider.ExtensionsFilter {
var extensions []dataprovider.ExtensionsFilter
for _, val := range portableAllowedExtensions {
p, exts := getExtensionsFilterValues(strings.TrimSpace(val))
if len(p) > 0 {
extensions = append(extensions, dataprovider.ExtensionsFilter{
Path: path.Clean(p),
AllowedExtensions: exts,
DeniedExtensions: []string{},
})
}
}
for _, val := range portableDeniedExtensions {
p, exts := getExtensionsFilterValues(strings.TrimSpace(val))
if len(p) > 0 {
found := false
for index, e := range extensions {
if path.Clean(e.Path) == path.Clean(p) {
extensions[index].DeniedExtensions = append(extensions[index].DeniedExtensions, exts...)
found = true
break
}
}
if !found {
extensions = append(extensions, dataprovider.ExtensionsFilter{
Path: path.Clean(p),
AllowedExtensions: []string{},
DeniedExtensions: exts,
})
}
}
}
return extensions
}
func getExtensionsFilterValues(value string) (string, []string) {
if strings.Contains(value, "::") {
dirExts := strings.Split(value, "::")
if len(dirExts) > 1 {
dir := strings.TrimSpace(dirExts[0])
exts := []string{}
for _, e := range strings.Split(dirExts[1], ",") {
cleanedExt := strings.TrimSpace(e)
if len(cleanedExt) > 0 {
exts = append(exts, cleanedExt)
}
}
if len(dir) > 0 && len(exts) > 0 {
return dir, exts
}
}
}
return "", nil
}

View File

@@ -57,5 +57,5 @@ These properties are stored inside the data provider.
If you want to use your existing accounts, you have these options: If you want to use your existing accounts, you have these options:
- If your accounts are aleady stored inside a supported database, you can create a database view. Since a view is read only, you have to disable user management and quota tracking so SFTPGo will never try to write to the view - If your accounts are aleady stored inside a supported database, you can create a database view. Since a view is read only, you have to disable user management and quota tracking so SFTPGo will never try to write to the view
- you can import your users inside SFTPGo. Take a look at [sftpgo_api_cli.py](../scripts/README.md "SFTPGo api cli script"), it can convert and import users from Linux system users and Pure-FTPd/ProFTPD virtual users - you can import your users inside SFTPGo. Take a look at [sftpgo_api_cli.py](../scripts#convert-users-from-other-stores "SFTPGo API CLI script"), it can convert and import users from Linux system users and Pure-FTPd/ProFTPD virtual users
- you can use an external authentication program - you can use an external authentication program

View File

@@ -16,6 +16,8 @@ Usage:
Flags: Flags:
-C, --advertise-credentials If the SFTP service is advertised via multicast DNS, this flag allows to put username/password inside the advertised TXT record -C, --advertise-credentials If the SFTP service is advertised via multicast DNS, this flag allows to put username/password inside the advertised TXT record
-S, --advertise-service Advertise SFTP service using multicast DNS (default true) -S, --advertise-service Advertise SFTP service using multicast DNS (default true)
--allowed-extensions stringArray Allowed file extensions case insensitive. The format is /dir::ext1,ext2. For example: "/somedir::.jpg,.png"
--denied-extensions stringArray Denied file extensions case insensitive. The format is /dir::ext1,ext2. For example: "/somedir::.jpg,.png"
-d, --directory string Path to the directory to serve. This can be an absolute path or a path relative to the current directory (default ".") -d, --directory string Path to the directory to serve. This can be an absolute path or a path relative to the current directory (default ".")
-f, --fs-provider int 0 means local filesystem, 1 Amazon S3 compatible, 2 Google Cloud Storage -f, --fs-provider int 0 means local filesystem, 1 Amazon S3 compatible, 2 Google Cloud Storage
--gcs-automatic-credentials int 0 means explicit credentials using a JSON credentials file, 1 automatic (default 1) --gcs-automatic-credentials int 0 means explicit credentials using a JSON credentials file, 1 automatic (default 1)
@@ -38,6 +40,7 @@ Flags:
-s, --sftpd-port int 0 means a random non privileged port -s, --sftpd-port int 0 means a random non privileged port
-c, --ssh-commands strings SSH commands to enable. "*" means any supported SSH command including scp (default [md5sum,sha1sum,cd,pwd]) -c, --ssh-commands strings SSH commands to enable. "*" means any supported SSH command including scp (default [md5sum,sha1sum,cd,pwd])
-u, --username string Leave empty to use an auto generated value -u, --username string Leave empty to use an auto generated value
``` ```
In portable mode, SFTPGo can advertise the SFTP service and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it. In portable mode, SFTPGo can advertise the SFTP service and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it.

View File

@@ -221,8 +221,9 @@ func (s *Service) StartPortableMode(sftpdPort int, enabledSSHCommands []string,
}() }()
logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+ logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
"permissions: %v, enabled ssh commands: %v", sftpdConf.BindPort, s.PortableUser.Username, s.PortableUser.Password, "permissions: %+v, enabled ssh commands: %v file extensions filters: %+v", sftpdConf.BindPort, s.PortableUser.Username,
s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions, sftpdConf.EnabledSSHCommands) s.PortableUser.Password, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions)
return nil return nil
} }