move kms implementation outside the sdk package

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-01-06 10:11:47 +01:00
parent 0a3d94f73d
commit 6d3d94a01f
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
43 changed files with 899 additions and 907 deletions

View file

@ -14,8 +14,8 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/service"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/version"
@ -157,18 +157,19 @@ Please take a look at the usage below to customize the serving parameters`,
Permissions: permissions,
HomeDir: portableDir,
Status: 1,
Filters: sdk.UserFilters{
},
Filters: dataprovider.UserFilters{
BaseUserFilters: sdk.BaseUserFilters{
FilePatterns: parsePatternsFilesFilters(),
},
},
FsConfig: vfs.Filesystem{
Provider: sdk.GetProviderByName(portableFsProvider),
S3Config: vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: portableS3Bucket,
Region: portableS3Region,
AccessKey: portableS3AccessKey,
AccessSecret: kms.NewPlainSecret(portableS3AccessSecret),
Endpoint: portableS3Endpoint,
StorageClass: portableS3StorageClass,
ACL: portableS3ACL,
@ -177,46 +178,45 @@ Please take a look at the usage below to customize the serving parameters`,
UploadConcurrency: portableS3ULConcurrency,
ForcePathStyle: portableS3ForcePathStyle,
},
AccessSecret: kms.NewPlainSecret(portableS3AccessSecret),
},
GCSConfig: vfs.GCSFsConfig{
GCSFsConfig: sdk.GCSFsConfig{
BaseGCSFsConfig: sdk.BaseGCSFsConfig{
Bucket: portableGCSBucket,
Credentials: kms.NewPlainSecret(portableGCSCredentials),
AutomaticCredentials: portableGCSAutoCredentials,
StorageClass: portableGCSStorageClass,
KeyPrefix: portableGCSKeyPrefix,
},
Credentials: kms.NewPlainSecret(portableGCSCredentials),
},
AzBlobConfig: vfs.AzBlobFsConfig{
AzBlobFsConfig: sdk.AzBlobFsConfig{
BaseAzBlobFsConfig: sdk.BaseAzBlobFsConfig{
Container: portableAzContainer,
AccountName: portableAzAccountName,
AccountKey: kms.NewPlainSecret(portableAzAccountKey),
Endpoint: portableAzEndpoint,
AccessTier: portableAzAccessTier,
SASURL: kms.NewPlainSecret(portableAzSASURL),
KeyPrefix: portableAzKeyPrefix,
UseEmulator: portableAzUseEmulator,
UploadPartSize: int64(portableAzULPartSize),
UploadConcurrency: portableAzULConcurrency,
},
AccountKey: kms.NewPlainSecret(portableAzAccountKey),
SASURL: kms.NewPlainSecret(portableAzSASURL),
},
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(portableCryptPassphrase),
},
},
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: portableSFTPEndpoint,
Username: portableSFTPUsername,
Password: kms.NewPlainSecret(portableSFTPPassword),
PrivateKey: kms.NewPlainSecret(portableSFTPPrivateKey),
Fingerprints: portableSFTPFingerprints,
Prefix: portableSFTPPrefix,
DisableCouncurrentReads: portableSFTPDisableConcurrentReads,
BufferSize: portableSFTPDBufferSize,
},
Password: kms.NewPlainSecret(portableSFTPPassword),
PrivateKey: kms.NewPlainSecret(portableSFTPPrivateKey),
},
},
},

View file

@ -28,24 +28,24 @@ func TestNewActionNotification(t *testing.T) {
}
user.FsConfig.Provider = sdk.LocalFilesystemProvider
user.FsConfig.S3Config = vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: "s3bucket",
Endpoint: "endpoint",
},
}
user.FsConfig.GCSConfig = vfs.GCSFsConfig{
GCSFsConfig: sdk.GCSFsConfig{
BaseGCSFsConfig: sdk.BaseGCSFsConfig{
Bucket: "gcsbucket",
},
}
user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
AzBlobFsConfig: sdk.AzBlobFsConfig{
BaseAzBlobFsConfig: sdk.BaseAzBlobFsConfig{
Container: "azcontainer",
Endpoint: "azendpoint",
},
}
user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: "sftpendpoint",
},
}

View file

@ -19,8 +19,8 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -731,9 +731,7 @@ func TestPostConnectHook(t *testing.T) {
func TestCryptoConvertFileInfo(t *testing.T) {
name := "name"
fs, err := vfs.NewCryptFs("connID1", os.TempDir(), "", vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret("secret"),
},
})
require.NoError(t, err)
cryptFs := fs.(*vfs.CryptFs)
@ -772,10 +770,8 @@ func TestFolderCopy(t *testing.T) {
folder.FsConfig = vfs.Filesystem{
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret("crypto secret"),
},
},
}
folderCopy = folder.GetACopy()
folder.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()

View file

@ -13,8 +13,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -419,12 +419,12 @@ func TestCheckParentDirsErrors(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.S3FilesystemProvider,
S3Config: vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: "buck",
Region: "us-east-1",
AccessKey: "key",
AccessSecret: kms.NewPlainSecret("s3secret"),
},
AccessSecret: kms.NewPlainSecret("s3secret"),
},
},
}

View file

@ -34,10 +34,10 @@ import (
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/httpclient"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -2758,7 +2758,7 @@ func TestBuiltinKeyboardInteractiveAuthentication(t *testing.T) {
configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -2926,11 +2926,11 @@ func TestSFTPLoopError(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user2.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
},
},
@ -2939,11 +2939,11 @@ func TestSFTPLoopError(t *testing.T) {
user2.FsConfig.Provider = sdk.SFTPFilesystemProvider
user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user1.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated)
@ -2995,11 +2995,11 @@ func TestNonLocalCrossRename(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: baseUser.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
},
},
@ -3014,11 +3014,9 @@ func TestNonLocalCrossRename(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
MappedPath: mappedPathCrypt,
},
VirtualPath: vdirCryptPath,
@ -3117,11 +3115,9 @@ func TestNonLocalCrossRenameNonLocalBaseUser(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
MappedPath: mappedPathCrypt,
},
VirtualPath: vdirCryptPath,

View file

@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -264,7 +264,7 @@ func TestTransferErrors(t *testing.T) {
func TestRemovePartialCryptoFile(t *testing.T) {
testFile := filepath.Join(os.TempDir(), "transfer_test_file")
fs, err := vfs.NewCryptFs("id", os.TempDir(), "", vfs.CryptFsConfig{CryptFsConfig: sdk.CryptFsConfig{Passphrase: kms.NewPlainSecret("secret")}})
fs, err := vfs.NewCryptFs("id", os.TempDir(), "", vfs.CryptFsConfig{Passphrase: kms.NewPlainSecret("secret")})
require.NoError(t, err)
u := dataprovider.User{
BaseUser: sdk.BaseUser{

View file

@ -16,10 +16,10 @@ import (
"github.com/drakkan/sftpgo/v2/ftpd"
"github.com/drakkan/sftpgo/v2/httpclient"
"github.com/drakkan/sftpgo/v2/httpd"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/smtp"
"github.com/drakkan/sftpgo/v2/telemetry"

View file

@ -19,7 +19,7 @@ import (
"github.com/drakkan/sftpgo/v2/httpd"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/smtp"
"github.com/drakkan/sftpgo/v2/util"
@ -467,8 +467,8 @@ func TestPluginsFromEnv(t *testing.T) {
os.Setenv("SFTPGO_PLUGINS__0__ARGS", "arg1,arg2")
os.Setenv("SFTPGO_PLUGINS__0__SHA256SUM", "0a71ded61fccd59c4f3695b51c1b3d180da8d2d77ea09ccee20dac242675c193")
os.Setenv("SFTPGO_PLUGINS__0__AUTO_MTLS", "1")
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__SCHEME", kms.SchemeAWS)
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__ENCRYPTED_STATUS", kms.SecretStatusAWS)
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__SCHEME", sdkkms.SchemeAWS)
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__ENCRYPTED_STATUS", sdkkms.SecretStatusAWS)
os.Setenv("SFTPGO_PLUGINS__0__AUTH_OPTIONS__SCOPE", "14")
t.Cleanup(func() {
os.Unsetenv("SFTPGO_PLUGINS__0__TYPE")
@ -510,8 +510,8 @@ func TestPluginsFromEnv(t *testing.T) {
require.Equal(t, "arg2", pluginConf.Args[1])
require.Equal(t, "0a71ded61fccd59c4f3695b51c1b3d180da8d2d77ea09ccee20dac242675c193", pluginConf.SHA256Sum)
require.True(t, pluginConf.AutoMTLS)
require.Equal(t, kms.SchemeAWS, pluginConf.KMSOptions.Scheme)
require.Equal(t, kms.SecretStatusAWS, pluginConf.KMSOptions.EncryptedStatus)
require.Equal(t, sdkkms.SchemeAWS, pluginConf.KMSOptions.Scheme)
require.Equal(t, sdkkms.SecretStatusAWS, pluginConf.KMSOptions.EncryptedStatus)
require.Equal(t, 14, pluginConf.AuthOptions.Scope)
configAsJSON, err := json.Marshal(pluginsConf)
@ -524,8 +524,8 @@ func TestPluginsFromEnv(t *testing.T) {
os.Setenv("SFTPGO_PLUGINS__0__CMD", "plugin_start_cmd1")
os.Setenv("SFTPGO_PLUGINS__0__ARGS", "")
os.Setenv("SFTPGO_PLUGINS__0__AUTO_MTLS", "0")
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__SCHEME", kms.SchemeVaultTransit)
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__ENCRYPTED_STATUS", kms.SecretStatusVaultTransit)
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__SCHEME", sdkkms.SchemeVaultTransit)
os.Setenv("SFTPGO_PLUGINS__0__KMS_OPTIONS__ENCRYPTED_STATUS", sdkkms.SecretStatusVaultTransit)
err = config.LoadConfig(configDir, confName)
assert.NoError(t, err)
pluginsConf = config.GetPluginsConfig()
@ -547,8 +547,8 @@ func TestPluginsFromEnv(t *testing.T) {
require.Len(t, pluginConf.Args, 0)
require.Equal(t, "0a71ded61fccd59c4f3695b51c1b3d180da8d2d77ea09ccee20dac242675c193", pluginConf.SHA256Sum)
require.False(t, pluginConf.AutoMTLS)
require.Equal(t, kms.SchemeVaultTransit, pluginConf.KMSOptions.Scheme)
require.Equal(t, kms.SecretStatusVaultTransit, pluginConf.KMSOptions.EncryptedStatus)
require.Equal(t, sdkkms.SchemeVaultTransit, pluginConf.KMSOptions.Scheme)
require.Equal(t, sdkkms.SecretStatusVaultTransit, pluginConf.KMSOptions.EncryptedStatus)
require.Equal(t, 14, pluginConf.AuthOptions.Scope)
err = os.Remove(configFilePath)

View file

@ -15,10 +15,9 @@ import (
passwordvalidator "github.com/wagslane/go-password-validator"
"golang.org/x/crypto/bcrypt"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
)
@ -52,14 +51,14 @@ var (
PermAdminViewEvents}
)
// TOTPConfig defines the time-based one time password configuration
type TOTPConfig struct {
// AdminTOTPConfig defines the time-based one time password configuration
type AdminTOTPConfig struct {
Enabled bool `json:"enabled,omitempty"`
ConfigName string `json:"config_name,omitempty"`
Secret *kms.Secret `json:"secret,omitempty"`
}
func (c *TOTPConfig) validate(username string) error {
func (c *AdminTOTPConfig) validate(username string) error {
if !c.Enabled {
c.ConfigName = ""
c.Secret = kms.NewEmptySecret()
@ -93,11 +92,11 @@ type AdminFilters struct {
// API key auth allows to impersonate this administrator with an API key
AllowAPIKeyAuth bool `json:"allow_api_key_auth,omitempty"`
// Time-based one time passwords configuration
TOTPConfig TOTPConfig `json:"totp_config,omitempty"`
TOTPConfig AdminTOTPConfig `json:"totp_config,omitempty"`
// Recovery codes to use if the user loses access to their second factor auth device.
// Each code can only be used once, you should use these codes to login and disable or
// reset 2FA for your account
RecoveryCodes []sdk.RecoveryCode `json:"recovery_codes,omitempty"`
RecoveryCodes []RecoveryCode `json:"recovery_codes,omitempty"`
}
// Admin defines a SFTPGo admin
@ -403,12 +402,12 @@ func (a *Admin) getACopy() Admin {
filters.TOTPConfig.ConfigName = a.Filters.TOTPConfig.ConfigName
filters.TOTPConfig.Secret = a.Filters.TOTPConfig.Secret.Clone()
copy(filters.AllowList, a.Filters.AllowList)
filters.RecoveryCodes = make([]sdk.RecoveryCode, 0)
filters.RecoveryCodes = make([]RecoveryCode, 0)
for _, code := range a.Filters.RecoveryCodes {
if code.Secret == nil {
code.Secret = kms.NewEmptySecret()
}
filters.RecoveryCodes = append(filters.RecoveryCodes, sdk.RecoveryCode{
filters.RecoveryCodes = append(filters.RecoveryCodes, RecoveryCode{
Secret: code.Secret.Clone(),
Used: code.Used,
})

View file

@ -46,12 +46,12 @@ import (
"golang.org/x/crypto/ssh"
"github.com/drakkan/sftpgo/v2/httpclient"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/metric"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -1153,7 +1153,7 @@ func HasAdmin() bool {
// AddAdmin adds a new SFTPGo admin
func AddAdmin(admin *Admin, executor, ipAddress string) error {
admin.Filters.RecoveryCodes = nil
admin.Filters.TOTPConfig = TOTPConfig{
admin.Filters.TOTPConfig = AdminTOTPConfig{
Enabled: false,
}
err := provider.addAdmin(admin)
@ -1199,7 +1199,7 @@ func UserExists(username string) (User, error) {
// AddUser adds a new SFTPGo user.
func AddUser(user *User, executor, ipAddress string) error {
user.Filters.RecoveryCodes = nil
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = UserTOTPConfig{
Enabled: false,
}
err := provider.addUser(user)
@ -1559,7 +1559,7 @@ func validateUserVirtualFolders(user *User) error {
return nil
}
func validateUserTOTPConfig(c *sdk.TOTPConfig, username string) error {
func validateUserTOTPConfig(c *UserTOTPConfig, username string) error {
if !c.Enabled {
c.ConfigName = ""
c.Secret = kms.NewEmptySecret()

View file

@ -13,7 +13,6 @@ import (
"github.com/cockroachdb/cockroach-go/v2/crdb"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -1386,7 +1385,7 @@ func getUserFromDbRow(row sqlScanner) (User, error) {
user.Permissions = perms
}
if filters.Valid {
var userFilters sdk.UserFilters
var userFilters UserFilters
err = json.Unmarshal([]byte(filters.String), &userFilters)
if err == nil {
user.Filters = userFilters

View file

@ -15,10 +15,10 @@ import (
"strings"
"time"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -79,9 +79,44 @@ var (
permsCreateAny = []string{PermUpload, PermCreateDirs}
)
// RecoveryCode defines a 2FA recovery code
type RecoveryCode struct {
Secret *kms.Secret `json:"secret"`
Used bool `json:"used,omitempty"`
}
// UserTOTPConfig defines the time-based one time password configuration
type UserTOTPConfig struct {
Enabled bool `json:"enabled,omitempty"`
ConfigName string `json:"config_name,omitempty"`
Secret *kms.Secret `json:"secret,omitempty"`
// TOTP will be required for the specified protocols.
// SSH protocol (SFTP/SCP/SSH commands) will ask for the TOTP passcode if the client uses keyboard interactive
// authentication.
// FTP have no standard way to support two factor authentication, if you
// enable the support for this protocol you have to add the TOTP passcode after the password.
// For example if your password is "password" and your one time passcode is
// "123456" you have to use "password123456" as password.
Protocols []string `json:"protocols,omitempty"`
}
// UserFilters defines additional restrictions for a user
// TODO: rename to UserOptions in v3
type UserFilters struct {
sdk.BaseUserFilters
// Time-based one time passwords configuration
TOTPConfig UserTOTPConfig `json:"totp_config,omitempty"`
// Recovery codes to use if the user loses access to their second factor auth device.
// Each code can only be used once, you should use these codes to login and disable or
// reset 2FA for your account
RecoveryCodes []RecoveryCode `json:"recovery_codes,omitempty"`
}
// User defines a SFTPGo user
type User struct {
sdk.BaseUser
// Additional restrictions
Filters UserFilters `json:"filters"`
// Mapping between virtual paths and virtual folders
VirtualFolders []vfs.VirtualFolder `json:"virtual_folders,omitempty"`
// Filesystem configuration details
@ -1168,7 +1203,7 @@ func (u *User) getACopy() User {
copy(perms, v)
permissions[k] = perms
}
filters := sdk.UserFilters{}
filters := UserFilters{}
filters.MaxUploadFileSize = u.Filters.MaxUploadFileSize
filters.TLSUsername = u.Filters.TLSUsername
filters.UserType = u.Filters.UserType
@ -1194,12 +1229,12 @@ func (u *User) getACopy() User {
filters.AllowAPIKeyAuth = u.Filters.AllowAPIKeyAuth
filters.WebClient = make([]string, len(u.Filters.WebClient))
copy(filters.WebClient, u.Filters.WebClient)
filters.RecoveryCodes = make([]sdk.RecoveryCode, 0, len(u.Filters.RecoveryCodes))
filters.RecoveryCodes = make([]RecoveryCode, 0, len(u.Filters.RecoveryCodes))
for _, code := range u.Filters.RecoveryCodes {
if code.Secret == nil {
code.Secret = kms.NewEmptySecret()
}
filters.RecoveryCodes = append(filters.RecoveryCodes, sdk.RecoveryCode{
filters.RecoveryCodes = append(filters.RecoveryCodes, RecoveryCode{
Secret: code.Secret.Clone(),
Used: code.Used,
})
@ -1238,12 +1273,12 @@ func (u *User) getACopy() User {
Status: u.Status,
ExpirationDate: u.ExpirationDate,
LastLogin: u.LastLogin,
Filters: filters,
AdditionalInfo: u.AdditionalInfo,
Description: u.Description,
CreatedAt: u.CreatedAt,
UpdatedAt: u.UpdatedAt,
},
Filters: filters,
VirtualFolders: virtualFolders,
FsConfig: u.FsConfig.GetACopy(),
}

View file

@ -18,8 +18,8 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
)
func TestBasicFTPHandlingCryptFs(t *testing.T) {

View file

@ -32,10 +32,11 @@ import (
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/ftpd"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -604,7 +605,7 @@ func TestMultiFactorAuth(t *testing.T) {
configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -1686,7 +1687,7 @@ func TestLoginWithDatabaseCredentials(t *testing.T) {
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus())
assert.NotEmpty(t, user.FsConfig.GCSConfig.Credentials.GetPayload())
assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetAdditionalData())
assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetKey())
@ -2808,11 +2809,9 @@ func TestNestedVirtualFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
MappedPath: mappedPathCrypt,
},
VirtualPath: vdirCryptPath,

View file

@ -83,7 +83,7 @@ func disableAdmin2FA(w http.ResponseWriter, r *http.Request) {
return
}
admin.Filters.RecoveryCodes = nil
admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: false,
}
if err := dataprovider.UpdateAdmin(&admin, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
@ -105,7 +105,7 @@ func updateAdmin(w http.ResponseWriter, r *http.Request) {
adminID := admin.ID
totpConfig := admin.Filters.TOTPConfig
recoveryCodes := admin.Filters.RecoveryCodes
admin.Filters.TOTPConfig = dataprovider.TOTPConfig{}
admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{}
admin.Filters.RecoveryCodes = nil
err = render.DecodeJSON(r.Body, &admin)
if err != nil {

View file

@ -8,9 +8,8 @@ import (
"github.com/go-chi/render"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
)
@ -81,10 +80,10 @@ func saveTOTPConfig(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
return
}
recoveryCodes := make([]sdk.RecoveryCode, 0, 12)
recoveryCodes := make([]dataprovider.RecoveryCode, 0, 12)
for i := 0; i < 12; i++ {
code := getNewRecoveryCode()
recoveryCodes = append(recoveryCodes, sdk.RecoveryCode{Secret: kms.NewPlainSecret(code)})
recoveryCodes = append(recoveryCodes, dataprovider.RecoveryCode{Secret: kms.NewPlainSecret(code)})
}
if claims.hasUserAudience() {
if err := saveUserTOTPConfig(claims.Username, r, recoveryCodes); err != nil {
@ -125,7 +124,7 @@ func getRecoveryCodes(w http.ResponseWriter, r *http.Request) {
return
}
recoveryCodes := make([]recoveryCode, 0, 12)
var accountRecoveryCodes []sdk.RecoveryCode
var accountRecoveryCodes []dataprovider.RecoveryCode
if claims.hasUserAudience() {
user, err := dataprovider.UserExists(claims.Username)
if err != nil {
@ -163,11 +162,11 @@ func generateRecoveryCodes(w http.ResponseWriter, r *http.Request) {
return
}
recoveryCodes := make([]string, 0, 12)
accountRecoveryCodes := make([]sdk.RecoveryCode, 0, 12)
accountRecoveryCodes := make([]dataprovider.RecoveryCode, 0, 12)
for i := 0; i < 12; i++ {
code := getNewRecoveryCode()
recoveryCodes = append(recoveryCodes, code)
accountRecoveryCodes = append(accountRecoveryCodes, sdk.RecoveryCode{Secret: kms.NewPlainSecret(code)})
accountRecoveryCodes = append(accountRecoveryCodes, dataprovider.RecoveryCode{Secret: kms.NewPlainSecret(code)})
}
if claims.hasUserAudience() {
user, err := dataprovider.UserExists(claims.Username)
@ -200,7 +199,7 @@ func getNewRecoveryCode() string {
return fmt.Sprintf("RC-%v", strings.ToUpper(util.GenerateUniqueID()))
}
func saveUserTOTPConfig(username string, r *http.Request, recoveryCodes []sdk.RecoveryCode) error {
func saveUserTOTPConfig(username string, r *http.Request, recoveryCodes []dataprovider.RecoveryCode) error {
user, err := dataprovider.UserExists(username)
if err != nil {
return err
@ -220,7 +219,7 @@ func saveUserTOTPConfig(username string, r *http.Request, recoveryCodes []sdk.Re
return dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr))
}
func saveAdminTOTPConfig(username string, r *http.Request, recoveryCodes []sdk.RecoveryCode) error {
func saveAdminTOTPConfig(username string, r *http.Request, recoveryCodes []dataprovider.RecoveryCode) error {
admin, err := dataprovider.AdminExists(username)
if err != nil {
return err

View file

@ -10,8 +10,8 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/smtp"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
@ -89,7 +89,7 @@ func disableUser2FA(w http.ResponseWriter, r *http.Request) {
return
}
user.Filters.RecoveryCodes = nil
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: false,
}
if err := dataprovider.UpdateUser(&user, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
@ -140,7 +140,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
user.FsConfig.GCSConfig = vfs.GCSFsConfig{}
user.FsConfig.CryptConfig = vfs.CryptFsConfig{}
user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{}
user.Filters.TOTPConfig = sdk.TOTPConfig{}
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{}
user.Filters.RecoveryCodes = nil
user.VirtualFolders = nil
err = render.DecodeJSON(r.Body, &user)

View file

@ -45,11 +45,12 @@ import (
"github.com/drakkan/sftpgo/v2/httpclient"
"github.com/drakkan/sftpgo/v2/httpd"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/smtp"
"github.com/drakkan/sftpgo/v2/util"
@ -826,7 +827,7 @@ func TestPermMFADisabled(t *testing.T) {
assert.NoError(t, err)
token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
userTOTPConfig := sdk.TOTPConfig{
userTOTPConfig := dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -867,7 +868,7 @@ func TestPermMFADisabled(t *testing.T) {
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
user.Filters.RecoveryCodes = []sdk.RecoveryCode{
user.Filters.RecoveryCodes = []dataprovider.RecoveryCode{
{
Secret: kms.NewPlainSecret(util.GenerateUniqueID()),
},
@ -901,7 +902,7 @@ func TestLoginUserAPITOTP(t *testing.T) {
assert.NoError(t, err)
token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
userTOTPConfig := sdk.TOTPConfig{
userTOTPConfig := dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -968,7 +969,7 @@ func TestLoginAdminAPITOTP(t *testing.T) {
assert.NoError(t, err)
altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
assert.NoError(t, err)
adminTOTPConfig := dataprovider.TOTPConfig{
adminTOTPConfig := dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -1510,13 +1511,13 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.S3Config.Bucket = "testbucket"
u.FsConfig.S3Config.Region = "eu-west-1" //nolint:goconst
u.FsConfig.S3Config.AccessKey = "access-key" //nolint:goconst
u.FsConfig.S3Config.AccessSecret = kms.NewSecret(kms.SecretStatusRedacted, "access-secret", "", "")
u.FsConfig.S3Config.AccessSecret = kms.NewSecret(sdkkms.SecretStatusRedacted, "access-secret", "", "")
u.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?a=b"
u.FsConfig.S3Config.StorageClass = "Standard" //nolint:goconst
u.FsConfig.S3Config.KeyPrefix = "/adir/subdir/"
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.S3Config.AccessSecret.SetStatus(kms.SecretStatusPlain)
u.FsConfig.S3Config.AccessSecret.SetStatus(sdkkms.SecretStatusPlain)
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.S3Config.KeyPrefix = ""
@ -1560,10 +1561,10 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.GCSConfig.Bucket = "abucket"
u.FsConfig.GCSConfig.StorageClass = "Standard"
u.FsConfig.GCSConfig.KeyPrefix = "/somedir/subdir/"
u.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusRedacted, "test", "", "") //nolint:goconst
u.FsConfig.GCSConfig.Credentials = kms.NewSecret(sdkkms.SecretStatusRedacted, "test", "", "") //nolint:goconst
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.GCSConfig.Credentials.SetStatus(kms.SecretStatusPlain)
u.FsConfig.GCSConfig.Credentials.SetStatus(sdkkms.SecretStatusPlain)
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/" //nolint:goconst
@ -1571,7 +1572,7 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.GCSConfig.AutomaticCredentials = 0
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusSecretBox, "invalid", "", "")
u.FsConfig.GCSConfig.Credentials = kms.NewSecret(sdkkms.SecretStatusSecretBox, "invalid", "", "")
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
@ -1580,7 +1581,7 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("http://foo\x7f.com/")
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.SASURL = kms.NewSecret(kms.SecretStatusRedacted, "key", "", "")
u.FsConfig.AzBlobConfig.SASURL = kms.NewSecret(sdkkms.SecretStatusRedacted, "key", "", "")
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.SASURL = kms.NewEmptySecret()
@ -1590,11 +1591,11 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.AzBlobConfig.Container = "container"
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.AccountKey = kms.NewSecret(kms.SecretStatusRedacted, "key", "", "")
u.FsConfig.AzBlobConfig.AccountKey = kms.NewSecret(sdkkms.SecretStatusRedacted, "key", "", "")
u.FsConfig.AzBlobConfig.KeyPrefix = "/amedir/subdir/"
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.AccountKey.SetStatus(kms.SecretStatusPlain)
u.FsConfig.AzBlobConfig.AccountKey.SetStatus(sdkkms.SecretStatusPlain)
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.KeyPrefix = "amedir/subdir/"
@ -1609,18 +1610,18 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.Provider = sdk.CryptedFilesystemProvider
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.CryptConfig.Passphrase = kms.NewSecret(kms.SecretStatusRedacted, "akey", "", "")
u.FsConfig.CryptConfig.Passphrase = kms.NewSecret(sdkkms.SecretStatusRedacted, "akey", "", "")
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u = getTestUser()
u.FsConfig.Provider = sdk.SFTPFilesystemProvider
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.SFTPConfig.Password = kms.NewSecret(kms.SecretStatusRedacted, "randompkey", "", "")
u.FsConfig.SFTPConfig.Password = kms.NewSecret(sdkkms.SecretStatusRedacted, "randompkey", "", "")
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.SFTPConfig.Password = kms.NewEmptySecret()
u.FsConfig.SFTPConfig.PrivateKey = kms.NewSecret(kms.SecretStatusRedacted, "keyforpkey", "", "")
u.FsConfig.SFTPConfig.PrivateKey = kms.NewSecret(sdkkms.SecretStatusRedacted, "keyforpkey", "", "")
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.SFTPConfig.PrivateKey = kms.NewPlainSecret("pk")
@ -1644,7 +1645,7 @@ func TestUserRedactedPassword(t *testing.T) {
u.FsConfig.S3Config.Bucket = "b"
u.FsConfig.S3Config.Region = "eu-west-1"
u.FsConfig.S3Config.AccessKey = "access-key"
u.FsConfig.S3Config.AccessSecret = kms.NewSecret(kms.SecretStatusRedacted, "access-secret", "", "")
u.FsConfig.S3Config.AccessSecret = kms.NewSecret(sdkkms.SecretStatusRedacted, "access-secret", "", "")
u.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?k=m"
u.FsConfig.S3Config.StorageClass = "Standard"
u.FsConfig.S3Config.ACL = "bucket-owner-full-control"
@ -1667,9 +1668,7 @@ func TestUserRedactedPassword(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewSecret(kms.SecretStatusRedacted, "crypted-secret", "", ""),
},
Passphrase: kms.NewSecret(sdkkms.SecretStatusRedacted, "crypted-secret", "", ""),
},
},
},
@ -2356,24 +2355,22 @@ func TestUserS3Config(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret("Crypted-Secret"),
},
},
},
},
VirtualPath: "/folderPath",
})
user, body, err := httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(body))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, user.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetAdditionalData())
assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetKey())
assert.Equal(t, 60, user.FsConfig.S3Config.DownloadPartMaxTime)
if assert.Len(t, user.VirtualFolders, 1) {
folder := user.VirtualFolders[0]
assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, folder.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, folder.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetKey())
@ -2382,7 +2379,7 @@ func TestUserS3Config(t *testing.T) {
assert.NoError(t, err)
folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, folder.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, folder.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetKey())
@ -2392,15 +2389,15 @@ func TestUserS3Config(t *testing.T) {
user.ID = 0
user.CreatedAt = 0
user.VirtualFolders = nil
secret := kms.NewSecret(kms.SecretStatusSecretBox, "Server-Access-Secret", "", "")
secret := kms.NewSecret(sdkkms.SecretStatusSecretBox, "Server-Access-Secret", "", "")
user.FsConfig.S3Config.AccessSecret = secret
_, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.Error(t, err)
user.FsConfig.S3Config.AccessSecret.SetStatus(kms.SecretStatusPlain)
user.FsConfig.S3Config.AccessSecret.SetStatus(sdkkms.SecretStatusPlain)
user, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err)
initialSecretPayload := user.FsConfig.S3Config.AccessSecret.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, initialSecretPayload)
assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetAdditionalData())
assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetKey())
@ -2414,7 +2411,7 @@ func TestUserS3Config(t *testing.T) {
user.FsConfig.S3Config.DownloadConcurrency = 4
user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, initialSecretPayload, user.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetAdditionalData())
assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetKey())
@ -2466,7 +2463,7 @@ func TestUserGCSConfig(t *testing.T) {
err = secret.Decrypt()
assert.NoError(t, err)
assert.Equal(t, "fake credentials", secret.GetPayload())
user.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusSecretBox, "fake encrypted credentials", "", "")
user.FsConfig.GCSConfig.Credentials = kms.NewSecret(sdkkms.SecretStatusSecretBox, "fake encrypted credentials", "", "")
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
assert.FileExists(t, credentialFile)
@ -2483,10 +2480,10 @@ func TestUserGCSConfig(t *testing.T) {
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
user.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusSecretBox, "fake credentials", "", "")
user.FsConfig.GCSConfig.Credentials = kms.NewSecret(sdkkms.SecretStatusSecretBox, "fake credentials", "", "")
_, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.Error(t, err)
user.FsConfig.GCSConfig.Credentials.SetStatus(kms.SecretStatusPlain)
user.FsConfig.GCSConfig.Credentials.SetStatus(sdkkms.SecretStatusPlain)
user, body, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(body))
err = os.RemoveAll(credentialsPath)
@ -2531,16 +2528,16 @@ func TestUserAzureBlobConfig(t *testing.T) {
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
initialPayload := user.FsConfig.AzBlobConfig.AccountKey.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
user.FsConfig.AzBlobConfig.AccountKey.SetStatus(kms.SecretStatusSecretBox)
user.FsConfig.AzBlobConfig.AccountKey.SetStatus(sdkkms.SecretStatusSecretBox)
user.FsConfig.AzBlobConfig.AccountKey.SetAdditionalData("data")
user.FsConfig.AzBlobConfig.AccountKey.SetKey("fake key")
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
@ -2550,7 +2547,7 @@ func TestUserAzureBlobConfig(t *testing.T) {
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
secret := kms.NewSecret(kms.SecretStatusSecretBox, "Server-Account-Key", "", "")
secret := kms.NewSecret(sdkkms.SecretStatusSecretBox, "Server-Account-Key", "", "")
user.FsConfig.AzBlobConfig.AccountKey = secret
_, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.Error(t, err)
@ -2558,7 +2555,7 @@ func TestUserAzureBlobConfig(t *testing.T) {
user, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err)
initialPayload = user.FsConfig.AzBlobConfig.AccountKey.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
@ -2569,7 +2566,7 @@ func TestUserAzureBlobConfig(t *testing.T) {
user.FsConfig.AzBlobConfig.UploadConcurrency = 5
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
@ -2593,25 +2590,25 @@ func TestUserAzureBlobConfig(t *testing.T) {
user.CreatedAt = 0
// sas test for add instead of update
user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
AzBlobFsConfig: sdk.AzBlobFsConfig{
BaseAzBlobFsConfig: sdk.BaseAzBlobFsConfig{
Container: user.FsConfig.AzBlobConfig.Container,
SASURL: kms.NewPlainSecret("http://127.0.0.1/fake/sass/url"),
},
SASURL: kms.NewPlainSecret("http://127.0.0.1/fake/sass/url"),
}
user, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err)
assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
initialPayload = user.FsConfig.AzBlobConfig.SASURL.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetKey())
user.FsConfig.AzBlobConfig.SASURL.SetStatus(kms.SecretStatusSecretBox)
user.FsConfig.AzBlobConfig.SASURL.SetStatus(sdkkms.SecretStatusSecretBox)
user.FsConfig.AzBlobConfig.SASURL.SetAdditionalData("data")
user.FsConfig.AzBlobConfig.SASURL.SetKey("fake key")
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.SASURL.GetPayload())
assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetKey())
@ -2628,16 +2625,16 @@ func TestUserCryptFs(t *testing.T) {
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
initialPayload := user.FsConfig.CryptConfig.Passphrase.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
user.FsConfig.CryptConfig.Passphrase.SetStatus(kms.SecretStatusSecretBox)
user.FsConfig.CryptConfig.Passphrase.SetStatus(sdkkms.SecretStatusSecretBox)
user.FsConfig.CryptConfig.Passphrase.SetAdditionalData("data")
user.FsConfig.CryptConfig.Passphrase.SetKey("fake pass key")
user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, initialPayload, user.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
@ -2647,7 +2644,7 @@ func TestUserCryptFs(t *testing.T) {
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
secret := kms.NewSecret(kms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
secret := kms.NewSecret(sdkkms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
user.FsConfig.CryptConfig.Passphrase = secret
_, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.Error(t, err)
@ -2655,7 +2652,7 @@ func TestUserCryptFs(t *testing.T) {
user, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err)
initialPayload = user.FsConfig.CryptConfig.Passphrase.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
@ -2663,7 +2660,7 @@ func TestUserCryptFs(t *testing.T) {
user.FsConfig.CryptConfig.Passphrase.SetKey("pass")
user, bb, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Equal(t, initialPayload, user.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
@ -2696,28 +2693,28 @@ func TestUserSFTPFs(t *testing.T) {
assert.Equal(t, int64(2), user.FsConfig.SFTPConfig.BufferSize)
initialPwdPayload := user.FsConfig.SFTPConfig.Password.GetPayload()
initialPkeyPayload := user.FsConfig.SFTPConfig.PrivateKey.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.Password.GetStatus())
assert.NotEmpty(t, initialPwdPayload)
assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetAdditionalData())
assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetKey())
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.NotEmpty(t, initialPkeyPayload)
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
user.FsConfig.SFTPConfig.Password.SetStatus(kms.SecretStatusSecretBox)
user.FsConfig.SFTPConfig.Password.SetStatus(sdkkms.SecretStatusSecretBox)
user.FsConfig.SFTPConfig.Password.SetAdditionalData("adata")
user.FsConfig.SFTPConfig.Password.SetKey("fake pwd key")
user.FsConfig.SFTPConfig.PrivateKey.SetStatus(kms.SecretStatusSecretBox)
user.FsConfig.SFTPConfig.PrivateKey.SetStatus(sdkkms.SecretStatusSecretBox)
user.FsConfig.SFTPConfig.PrivateKey.SetAdditionalData("adata")
user.FsConfig.SFTPConfig.PrivateKey.SetKey("fake key")
user.FsConfig.SFTPConfig.DisableCouncurrentReads = false
user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, initialPwdPayload, user.FsConfig.SFTPConfig.Password.GetPayload())
assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetAdditionalData())
assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetKey())
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, initialPkeyPayload, user.FsConfig.SFTPConfig.PrivateKey.GetPayload())
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
@ -2728,7 +2725,7 @@ func TestUserSFTPFs(t *testing.T) {
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
secret := kms.NewSecret(kms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
secret := kms.NewSecret(sdkkms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
user.FsConfig.SFTPConfig.Password = secret
_, _, err = httpdtest.AddUser(user, http.StatusCreated)
assert.Error(t, err)
@ -2742,7 +2739,7 @@ func TestUserSFTPFs(t *testing.T) {
assert.NoError(t, err)
initialPkeyPayload = user.FsConfig.SFTPConfig.PrivateKey.GetPayload()
assert.Nil(t, user.FsConfig.SFTPConfig.Password)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.NotEmpty(t, initialPkeyPayload)
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
@ -2750,7 +2747,7 @@ func TestUserSFTPFs(t *testing.T) {
user.FsConfig.SFTPConfig.PrivateKey.SetKey("k")
user, bb, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.NotEmpty(t, initialPkeyPayload)
assert.Equal(t, initialPkeyPayload, user.FsConfig.SFTPConfig.PrivateKey.GetPayload())
assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
@ -2882,7 +2879,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetPayload())
err = user1.FsConfig.S3Config.AccessSecret.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user1.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, user1.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, u1.FsConfig.S3Config.AccessSecret.GetPayload(), user1.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, user1.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, user1.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -2896,7 +2893,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetPayload())
err = user2.FsConfig.GCSConfig.Credentials.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user2.FsConfig.GCSConfig.Credentials.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, user2.FsConfig.GCSConfig.Credentials.GetStatus())
assert.Equal(t, u2.FsConfig.GCSConfig.Credentials.GetPayload(), user2.FsConfig.GCSConfig.Credentials.GetPayload())
assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetKey())
assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetAdditionalData())
@ -2910,7 +2907,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetPayload())
err = user3.FsConfig.AzBlobConfig.AccountKey.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user3.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, user3.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, u3.FsConfig.AzBlobConfig.AccountKey.GetPayload(), user3.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetKey())
assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
@ -2924,7 +2921,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetPayload())
err = user4.FsConfig.CryptConfig.Passphrase.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, u4.FsConfig.CryptConfig.Passphrase.GetPayload(), user4.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
@ -2938,7 +2935,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetPayload())
err = user5.FsConfig.SFTPConfig.Password.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user5.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, user5.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, u5.FsConfig.SFTPConfig.Password.GetPayload(), user5.FsConfig.SFTPConfig.Password.GetPayload())
assert.Empty(t, user5.FsConfig.SFTPConfig.Password.GetKey())
assert.Empty(t, user5.FsConfig.SFTPConfig.Password.GetAdditionalData())
@ -2948,7 +2945,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetPayload())
err = user5.FsConfig.SFTPConfig.PrivateKey.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user5.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, user5.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, u5.FsConfig.SFTPConfig.PrivateKey.GetPayload(), user5.FsConfig.SFTPConfig.PrivateKey.GetPayload())
assert.Empty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetKey())
assert.Empty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
@ -2996,13 +2993,13 @@ func TestSecretObject(t *testing.T) {
require.True(t, s.IsValid())
err := s.Encrypt()
require.NoError(t, err)
require.Equal(t, kms.SecretStatusSecretBox, s.GetStatus())
require.Equal(t, sdkkms.SecretStatusSecretBox, s.GetStatus())
require.NotEmpty(t, s.GetPayload())
require.NotEmpty(t, s.GetKey())
require.True(t, s.IsValid())
err = s.Decrypt()
require.NoError(t, err)
require.Equal(t, kms.SecretStatusPlain, s.GetStatus())
require.Equal(t, sdkkms.SecretStatusPlain, s.GetStatus())
require.Equal(t, "test data", s.GetPayload())
require.Empty(t, s.GetKey())
}
@ -3017,10 +3014,10 @@ func TestSecretObjectCompatibility(t *testing.T) {
localAsJSON, err := json.Marshal(s)
assert.NoError(t, err)
for _, secretStatus := range []string{kms.SecretStatusSecretBox} {
for _, secretStatus := range []string{sdkkms.SecretStatusSecretBox} {
kmsConfig := config.GetKMSConfig()
assert.Empty(t, kmsConfig.Secrets.MasterKeyPath)
if secretStatus == kms.SecretStatusVaultTransit {
if secretStatus == sdkkms.SecretStatusVaultTransit {
os.Setenv("VAULT_SERVER_URL", "http://127.0.0.1:8200")
os.Setenv("VAULT_SERVER_TOKEN", "s.9lYGq83MbgG5KR5kfebXVyhJ")
kmsConfig.Secrets.URL = "hashivault://mykey"
@ -3037,20 +3034,20 @@ func TestSecretObjectCompatibility(t *testing.T) {
err = secretClone.Decrypt()
assert.NoError(t, err)
assert.Equal(t, testPayload, secretClone.GetPayload())
if secretStatus == kms.SecretStatusVaultTransit {
if secretStatus == sdkkms.SecretStatusVaultTransit {
// decrypt the local secret now that the provider is vault
secretLocal := kms.NewEmptySecret()
err = json.Unmarshal(localAsJSON, secretLocal)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, 0, secretLocal.GetMode())
err = secretLocal.Decrypt()
assert.NoError(t, err)
assert.Equal(t, testPayload, secretLocal.GetPayload())
assert.Equal(t, kms.SecretStatusPlain, secretLocal.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, secretLocal.GetStatus())
err = secretLocal.Encrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, 0, secretLocal.GetMode())
}
@ -3065,7 +3062,7 @@ func TestSecretObjectCompatibility(t *testing.T) {
MasterKeyPath: masterKeyPath,
},
}
if secretStatus == kms.SecretStatusVaultTransit {
if secretStatus == sdkkms.SecretStatusVaultTransit {
config.Secrets.URL = "hashivault://mykey"
}
err = config.Initialize()
@ -3085,22 +3082,22 @@ func TestSecretObjectCompatibility(t *testing.T) {
err = secret.Decrypt()
assert.NoError(t, err)
assert.Equal(t, testPayload, secret.GetPayload())
if secretStatus == kms.SecretStatusVaultTransit {
if secretStatus == sdkkms.SecretStatusVaultTransit {
// decrypt the local secret encryped without a master key now that
// the provider is vault and a master key is set.
// The provider will not change, the master key will be used
secretLocal := kms.NewEmptySecret()
err = json.Unmarshal(localAsJSON, secretLocal)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, 0, secretLocal.GetMode())
err = secretLocal.Decrypt()
assert.NoError(t, err)
assert.Equal(t, testPayload, secretLocal.GetPayload())
assert.Equal(t, kms.SecretStatusPlain, secretLocal.GetStatus())
assert.Equal(t, sdkkms.SecretStatusPlain, secretLocal.GetStatus())
err = secretLocal.Encrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, secretLocal.GetStatus())
assert.Equal(t, 1, secretLocal.GetMode())
}
@ -3108,7 +3105,7 @@ func TestSecretObjectCompatibility(t *testing.T) {
assert.NoError(t, err)
err = os.Remove(masterKeyPath)
assert.NoError(t, err)
if secretStatus == kms.SecretStatusVaultTransit {
if secretStatus == sdkkms.SecretStatusVaultTransit {
os.Unsetenv("VAULT_SERVER_URL")
os.Unsetenv("VAULT_SERVER_TOKEN")
}
@ -3373,13 +3370,13 @@ func TestEmbeddedFoldersUpdate(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.S3FilesystemProvider,
S3Config: vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: "test",
Region: "us-east-1",
AccessKey: "akey",
AccessSecret: kms.NewPlainSecret("asecret"),
Endpoint: "http://127.0.1.1:9090",
},
AccessSecret: kms.NewPlainSecret("asecret"),
},
},
},
@ -3399,7 +3396,7 @@ func TestEmbeddedFoldersUpdate(t *testing.T) {
assert.Equal(t, "test", userFolder.FsConfig.S3Config.Bucket)
assert.Equal(t, "us-east-1", userFolder.FsConfig.S3Config.Region)
assert.Equal(t, "http://127.0.1.1:9090", userFolder.FsConfig.S3Config.Endpoint)
assert.Equal(t, kms.SecretStatusSecretBox, userFolder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, userFolder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, userFolder.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -3414,7 +3411,7 @@ func TestEmbeddedFoldersUpdate(t *testing.T) {
assert.Equal(t, "test", folder.FsConfig.S3Config.Bucket)
assert.Equal(t, "us-east-1", folder.FsConfig.S3Config.Region)
assert.Equal(t, "http://127.0.1.1:9090", folder.FsConfig.S3Config.Endpoint)
assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, folder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, folder.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -3432,7 +3429,7 @@ func TestEmbeddedFoldersUpdate(t *testing.T) {
assert.Equal(t, "test", folder.FsConfig.S3Config.Bucket)
assert.Equal(t, "us-east-1", folder.FsConfig.S3Config.Region)
assert.Equal(t, "http://127.0.1.1:9090", folder.FsConfig.S3Config.Endpoint)
assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, folder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, folder.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -3449,7 +3446,7 @@ func TestEmbeddedFoldersUpdate(t *testing.T) {
assert.Equal(t, "test", userFolder.FsConfig.S3Config.Bucket)
assert.Equal(t, "us-east-1", userFolder.FsConfig.S3Config.Region)
assert.Equal(t, "http://127.0.1.1:9090", userFolder.FsConfig.S3Config.Endpoint)
assert.Equal(t, kms.SecretStatusSecretBox, userFolder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, userFolder.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, userFolder.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -3775,7 +3772,7 @@ func TestSaveErrors(t *testing.T) {
assert.NoError(t, err)
recCode := "recovery code"
recoveryCodes := []sdk.RecoveryCode{
recoveryCodes := []dataprovider.RecoveryCode{
{
Secret: kms.NewPlainSecret(recCode),
Used: false,
@ -3791,7 +3788,7 @@ func TestSaveErrors(t *testing.T) {
configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = u.Password
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -3813,7 +3810,7 @@ func TestSaveErrors(t *testing.T) {
admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
assert.NoError(t, err)
admin.Password = a.Password
admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -4141,11 +4138,9 @@ func TestFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret("asecret"),
},
},
},
}
_, _, err := httpdtest.AddFolder(folder, http.StatusBadRequest)
assert.NoError(t, err)
@ -4157,7 +4152,7 @@ func TestFolders(t *testing.T) {
assert.Equal(t, 0, folder1.UsedQuotaFiles)
assert.Equal(t, int64(0), folder1.UsedQuotaSize)
assert.Equal(t, int64(0), folder1.LastQuotaUpdate)
assert.Equal(t, kms.SecretStatusSecretBox, folder1.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, folder1.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, folder1.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, folder1.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, folder1.FsConfig.CryptConfig.Passphrase.GetKey())
@ -4185,7 +4180,7 @@ func TestFolders(t *testing.T) {
if f.Name == folder1.Name {
found = true
assert.Equal(t, folder1.MappedPath, f.MappedPath)
assert.Equal(t, kms.SecretStatusSecretBox, f.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, f.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, f.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetKey())
@ -4203,7 +4198,7 @@ func TestFolders(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, folder1.Name, f.Name)
assert.Equal(t, folder1.MappedPath, f.MappedPath)
assert.Equal(t, kms.SecretStatusSecretBox, f.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, f.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, f.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetKey())
@ -5086,7 +5081,7 @@ func TestAdminTwoFactorLogin(t *testing.T) {
assert.NoError(t, err)
altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
assert.NoError(t, err)
adminTOTPConfig := dataprovider.TOTPConfig{
adminTOTPConfig := dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -5118,7 +5113,7 @@ func TestAdminTwoFactorLogin(t *testing.T) {
for _, c := range admin.Filters.RecoveryCodes {
assert.Empty(t, c.Secret.GetAdditionalData())
assert.Empty(t, c.Secret.GetKey())
assert.Equal(t, kms.SecretStatusSecretBox, c.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, c.Secret.GetStatus())
assert.NotEmpty(t, c.Secret.GetPayload())
}
@ -5409,7 +5404,7 @@ func TestAdminTOTP(t *testing.T) {
admin.Username = altAdminUsername
admin.Password = altAdminPassword
// TOTPConfig will be ignored on add
admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: "config",
Secret: kms.NewEmptySecret(),
@ -5478,7 +5473,7 @@ func TestAdminTOTP(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "this passcode was already used")
adminTOTPConfig := dataprovider.TOTPConfig{
adminTOTPConfig := dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: totpGenResp.ConfigName,
Secret: kms.NewPlainSecret(totpGenResp.Secret),
@ -5497,13 +5492,13 @@ func TestAdminTOTP(t *testing.T) {
assert.Empty(t, admin.Filters.TOTPConfig.Secret.GetKey())
assert.Empty(t, admin.Filters.TOTPConfig.Secret.GetAdditionalData())
assert.NotEmpty(t, admin.Filters.TOTPConfig.Secret.GetPayload())
assert.Equal(t, kms.SecretStatusSecretBox, admin.Filters.TOTPConfig.Secret.GetStatus())
admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
assert.Equal(t, sdkkms.SecretStatusSecretBox, admin.Filters.TOTPConfig.Secret.GetStatus())
admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: false,
ConfigName: util.GenerateUniqueID(),
Secret: kms.NewEmptySecret(),
}
admin.Filters.RecoveryCodes = []sdk.RecoveryCode{
admin.Filters.RecoveryCodes = []dataprovider.RecoveryCode{
{
Secret: kms.NewEmptySecret(),
},
@ -5655,7 +5650,7 @@ func TestWebUserTwoFactorLogin(t *testing.T) {
webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
userTOTPConfig := sdk.TOTPConfig{
userTOTPConfig := dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -5685,7 +5680,7 @@ func TestWebUserTwoFactorLogin(t *testing.T) {
for _, c := range user.Filters.RecoveryCodes {
assert.Empty(t, c.Secret.GetAdditionalData())
assert.Empty(t, c.Secret.GetKey())
assert.Equal(t, kms.SecretStatusSecretBox, c.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, c.Secret.GetStatus())
assert.NotEmpty(t, c.Secret.GetPayload())
}
@ -6111,7 +6106,7 @@ func TestMFAErrors(t *testing.T) {
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
// invalid TOTP config name
userTOTPConfig := sdk.TOTPConfig{
userTOTPConfig := dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: "missing name",
Secret: kms.NewPlainSecret(xid.New().String()),
@ -6126,7 +6121,7 @@ func TestMFAErrors(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "totp: config name")
// invalid TOTP secret
userTOTPConfig = sdk.TOTPConfig{
userTOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: nil,
@ -6141,7 +6136,7 @@ func TestMFAErrors(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "totp: secret is mandatory")
// no protocol
userTOTPConfig = sdk.TOTPConfig{
userTOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: kms.NewPlainSecret(xid.New().String()),
@ -6156,7 +6151,7 @@ func TestMFAErrors(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "totp: specify at least one protocol")
// invalid protocol
userTOTPConfig = sdk.TOTPConfig{
userTOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: kms.NewPlainSecret(xid.New().String()),
@ -6171,7 +6166,7 @@ func TestMFAErrors(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "totp: invalid protocol")
adminTOTPConfig := dataprovider.TOTPConfig{
adminTOTPConfig := dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: "",
Secret: kms.NewPlainSecret("secret"),
@ -6185,7 +6180,7 @@ func TestMFAErrors(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "totp: config name is mandatory")
adminTOTPConfig = dataprovider.TOTPConfig{
adminTOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: nil,
@ -6200,10 +6195,10 @@ func TestMFAErrors(t *testing.T) {
assert.Contains(t, rr.Body.String(), "totp: secret is mandatory")
// invalid TOTP secret status
userTOTPConfig = sdk.TOTPConfig{
userTOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: kms.NewSecret(kms.SecretStatusRedacted, "", "", ""),
Secret: kms.NewSecret(sdkkms.SecretStatusRedacted, "", "", ""),
Protocols: []string{common.ProtocolSSH},
}
asJSON, err = json.Marshal(userTOTPConfig)
@ -6237,15 +6232,15 @@ func TestMFAInvalidSecret(t *testing.T) {
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
Secret: kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", user.Username),
Protocols: []string{common.ProtocolSSH, common.ProtocolHTTP},
}
user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, sdk.RecoveryCode{
user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, dataprovider.RecoveryCode{
Used: false,
Secret: kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
Secret: kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", user.Username),
})
err = dataprovider.UpdateUser(&user, "", "")
assert.NoError(t, err)
@ -6310,14 +6305,14 @@ func TestMFAInvalidSecret(t *testing.T) {
assert.NoError(t, err)
admin.Password = altAdminPassword
admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
Secret: kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
Secret: kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", user.Username),
}
admin.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, sdk.RecoveryCode{
admin.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, dataprovider.RecoveryCode{
Used: false,
Secret: kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
Secret: kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", user.Username),
})
err = dataprovider.UpdateAdmin(&admin, "", "")
assert.NoError(t, err)
@ -6370,7 +6365,7 @@ func TestMFAInvalidSecret(t *testing.T) {
func TestWebUserTOTP(t *testing.T) {
u := getTestUser()
// TOTPConfig will be ignored on add
u.Filters.TOTPConfig = sdk.TOTPConfig{
u.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: "",
Secret: kms.NewEmptySecret(),
@ -6430,7 +6425,7 @@ func TestWebUserTOTP(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "this passcode was already used")
userTOTPConfig := sdk.TOTPConfig{
userTOTPConfig := dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: totpGenResp.ConfigName,
Secret: kms.NewPlainSecret(totpGenResp.Secret),
@ -6453,11 +6448,11 @@ func TestWebUserTOTP(t *testing.T) {
assert.Empty(t, totpCfg.Secret.GetKey())
assert.Empty(t, totpCfg.Secret.GetAdditionalData())
assert.NotEmpty(t, secretPayload)
assert.Equal(t, kms.SecretStatusSecretBox, totpCfg.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, totpCfg.Secret.GetStatus())
assert.Len(t, totpCfg.Protocols, 1)
assert.Contains(t, totpCfg.Protocols, common.ProtocolSSH)
// update protocols only
userTOTPConfig = sdk.TOTPConfig{
userTOTPConfig = dataprovider.UserTOTPConfig{
Protocols: []string{common.ProtocolSSH, common.ProtocolFTP},
Secret: kms.NewEmptySecret(),
}
@ -6470,7 +6465,7 @@ func TestWebUserTOTP(t *testing.T) {
checkResponseCode(t, http.StatusOK, rr)
// update the user, TOTP should not be affected
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: false,
Secret: kms.NewEmptySecret(),
}
@ -6484,7 +6479,7 @@ func TestWebUserTOTP(t *testing.T) {
assert.Empty(t, user.Filters.TOTPConfig.Secret.GetKey())
assert.Empty(t, user.Filters.TOTPConfig.Secret.GetAdditionalData())
assert.Equal(t, secretPayload, user.Filters.TOTPConfig.Secret.GetPayload())
assert.Equal(t, kms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
assert.Len(t, user.Filters.TOTPConfig.Protocols, 2)
assert.Contains(t, user.Filters.TOTPConfig.Protocols, common.ProtocolSSH)
assert.Contains(t, user.Filters.TOTPConfig.Protocols, common.ProtocolFTP)
@ -8520,21 +8515,21 @@ func TestSFTPLoopError(t *testing.T) {
user1.FsConfig = vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user2.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
}
user2.FsConfig.Provider = sdk.SFTPFilesystemProvider
user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user1.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated)
@ -12644,7 +12639,7 @@ func TestWebAdminBasicMock(t *testing.T) {
assert.NoError(t, err)
altToken, err := getJWTWebTokenFromTestServer(altAdminUsername, altAdminPassword)
assert.NoError(t, err)
adminTOTPConfig := dataprovider.TOTPConfig{
adminTOTPConfig := dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -12672,7 +12667,7 @@ func TestWebAdminBasicMock(t *testing.T) {
secretPayload := admin.Filters.TOTPConfig.Secret.GetPayload()
assert.NotEmpty(t, secretPayload)
adminTOTPConfig = dataprovider.TOTPConfig{
adminTOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewEmptySecret(),
@ -12691,7 +12686,7 @@ func TestWebAdminBasicMock(t *testing.T) {
assert.True(t, admin.Filters.TOTPConfig.Enabled)
assert.Equal(t, secretPayload, admin.Filters.TOTPConfig.Secret.GetPayload())
adminTOTPConfig = dataprovider.TOTPConfig{
adminTOTPConfig = dataprovider.AdminTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: nil,
@ -13374,7 +13369,7 @@ func TestWebUserUpdateMock(t *testing.T) {
assert.NoError(t, err)
userToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
userTOTPConfig := sdk.TOTPConfig{
userTOTPConfig := dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -14131,7 +14126,7 @@ func TestWebUserS3Mock(t *testing.T) {
assert.Equal(t, updateUser.FsConfig.S3Config.DownloadConcurrency, user.FsConfig.S3Config.DownloadConcurrency)
assert.True(t, updateUser.FsConfig.S3Config.ForcePathStyle)
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, updateUser.FsConfig.S3Config.AccessSecret.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, updateUser.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, updateUser.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -14157,7 +14152,7 @@ func TestWebUserS3Mock(t *testing.T) {
var lastUpdatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetStatus())
assert.Equal(t, updateUser.FsConfig.S3Config.AccessSecret.GetPayload(), lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetAdditionalData())
@ -14392,7 +14387,7 @@ func TestWebUserAzureBlobMock(t *testing.T) {
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadPartSize, user.FsConfig.AzBlobConfig.UploadPartSize)
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadConcurrency, user.FsConfig.AzBlobConfig.UploadConcurrency)
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetKey())
assert.Empty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
@ -14411,7 +14406,7 @@ func TestWebUserAzureBlobMock(t *testing.T) {
var lastUpdatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetPayload(), lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
@ -14434,7 +14429,7 @@ func TestWebUserAzureBlobMock(t *testing.T) {
updateUser = dataprovider.User{}
err = render.DecodeJSON(rr.Body, &updateUser)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetPayload())
assert.Empty(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetKey())
assert.Empty(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
@ -14453,7 +14448,7 @@ func TestWebUserAzureBlobMock(t *testing.T) {
lastUpdatedUser = dataprovider.User{}
err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetStatus())
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetPayload(), lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
@ -14530,7 +14525,7 @@ func TestWebUserCryptMock(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, updateUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
@ -14549,7 +14544,7 @@ func TestWebUserCryptMock(t *testing.T) {
var lastUpdatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload(), lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
@ -14639,11 +14634,11 @@ func TestWebUserSFTPFsMock(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.Password.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.SFTPConfig.Password.GetPayload())
assert.Empty(t, updateUser.FsConfig.SFTPConfig.Password.GetKey())
assert.Empty(t, updateUser.FsConfig.SFTPConfig.Password.GetAdditionalData())
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetPayload())
assert.Empty(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetKey())
assert.Empty(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
@ -14670,11 +14665,11 @@ func TestWebUserSFTPFsMock(t *testing.T) {
var lastUpdatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetStatus())
assert.Equal(t, updateUser.FsConfig.SFTPConfig.Password.GetPayload(), lastUpdatedUser.FsConfig.SFTPConfig.Password.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetAdditionalData())
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetStatus())
assert.Equal(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetPayload(), lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())

View file

@ -33,9 +33,9 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -1773,10 +1773,10 @@ func TestConnection(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.GCSFilesystemProvider,
GCSConfig: vfs.GCSFsConfig{
GCSFsConfig: sdk.GCSFsConfig{
BaseGCSFsConfig: sdk.BaseGCSFsConfig{
Bucket: "test_bucket_name",
Credentials: kms.NewPlainSecret("invalid JSON payload"),
},
Credentials: kms.NewPlainSecret("invalid JSON payload"),
},
},
}
@ -1815,12 +1815,12 @@ func TestGetFileWriterErrors(t *testing.T) {
user.FsConfig.Provider = sdk.S3FilesystemProvider
user.FsConfig.S3Config = vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: "b",
Region: "us-west-1",
AccessKey: "key",
AccessSecret: kms.NewPlainSecret("secret"),
},
AccessSecret: kms.NewPlainSecret("secret"),
}
connection = &Connection{
BaseConnection: common.NewBaseConnection(xid.New().String(), common.ProtocolHTTP, "", "", user),

View file

@ -16,9 +16,10 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/smtp"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/version"
@ -179,7 +180,7 @@ type changePasswordPage struct {
type mfaPage struct {
basePage
TOTPConfigs []string
TOTPConfig dataprovider.TOTPConfig
TOTPConfig dataprovider.AdminTOTPConfig
GenerateTOTPURL string
ValidateTOTPURL string
SaveTOTPURL string
@ -821,8 +822,8 @@ func getFilePatternsFromPostField(r *http.Request) []sdk.PatternsFilter {
return result
}
func getFiltersFromUserPostFields(r *http.Request) (sdk.UserFilters, error) {
var filters sdk.UserFilters
func getFiltersFromUserPostFields(r *http.Request) (sdk.BaseUserFilters, error) {
var filters sdk.BaseUserFilters
bwLimits, err := getBandwidthLimitsFromPostFields(r)
if err != nil {
return filters, err
@ -853,7 +854,7 @@ func getFiltersFromUserPostFields(r *http.Request) (sdk.UserFilters, error) {
func getSecretFromFormField(r *http.Request, field string) *kms.Secret {
secret := kms.NewPlainSecret(r.Form.Get(field))
if strings.TrimSpace(secret.GetPayload()) == redactedSecret {
secret.SetStatus(kms.SecretStatusRedacted)
secret.SetStatus(sdkkms.SecretStatusRedacted)
}
if strings.TrimSpace(secret.GetPayload()) == "" {
secret.SetStatus("")
@ -1204,10 +1205,12 @@ func getUserFromPostFields(r *http.Request) (dataprovider.User, error) {
DownloadBandwidth: bandwidthDL,
Status: status,
ExpirationDate: expirationDateMillis,
Filters: filters,
AdditionalInfo: r.Form.Get("additional_info"),
Description: r.Form.Get("description"),
},
Filters: dataprovider.UserFilters{
BaseUserFilters: filters,
},
VirtualFolders: getVirtualFoldersFromPostFields(r),
FsConfig: fsConfig,
}

View file

@ -158,7 +158,7 @@ type changeClientPasswordPage struct {
type clientMFAPage struct {
baseClientPage
TOTPConfigs []string
TOTPConfig sdk.TOTPConfig
TOTPConfig dataprovider.UserTOTPConfig
GenerateTOTPURL string
ValidateTOTPURL string
SaveTOTPURL string

View file

@ -20,7 +20,7 @@ import (
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/httpclient"
"github.com/drakkan/sftpgo/v2/httpd"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/version"
"github.com/drakkan/sftpgo/v2/vfs"

View file

@ -1,8 +1,12 @@
package kms
import (
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
)
// BaseSecret defines the base struct shared among all the secret providers
type BaseSecret struct {
Status SecretStatus `json:"status,omitempty"`
Status sdkkms.SecretStatus `json:"status,omitempty"`
Payload string `json:"payload,omitempty"`
Key string `json:"key,omitempty"`
AdditionalData string `json:"additional_data,omitempty"`
@ -11,7 +15,7 @@ type BaseSecret struct {
}
// GetStatus returns the secret's status
func (s *BaseSecret) GetStatus() SecretStatus {
func (s *BaseSecret) GetStatus() sdkkms.SecretStatus {
return s.Status
}
@ -46,7 +50,7 @@ func (s *BaseSecret) SetAdditionalData(value string) {
}
// SetStatus sets the secret's status
func (s *BaseSecret) SetStatus(value SecretStatus) {
func (s *BaseSecret) SetStatus(value sdkkms.SecretStatus) {
s.Status = value
}

View file

@ -8,6 +8,8 @@ import (
"encoding/hex"
"errors"
"io"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
)
var (
@ -19,7 +21,7 @@ type builtinSecret struct {
}
func init() {
RegisterSecretProvider(SchemeBuiltin, SecretStatusAES256GCM, newBuiltinSecret)
RegisterSecretProvider(sdkkms.SchemeBuiltin, sdkkms.SecretStatusAES256GCM, newBuiltinSecret)
}
func newBuiltinSecret(base BaseSecret, url, masterKey string) SecretProvider {
@ -33,7 +35,7 @@ func (s *builtinSecret) Name() string {
}
func (s *builtinSecret) IsEncrypted() bool {
return s.Status == SecretStatusAES256GCM
return s.Status == sdkkms.SecretStatusAES256GCM
}
func (s *builtinSecret) deriveKey(key []byte) []byte {
@ -52,7 +54,7 @@ func (s *builtinSecret) Encrypt() error {
return ErrInvalidSecret
}
switch s.Status {
case SecretStatusPlain:
case sdkkms.SecretStatusPlain:
key := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return err
@ -76,7 +78,7 @@ func (s *builtinSecret) Encrypt() error {
ciphertext := gcm.Seal(nonce, nonce, []byte(s.Payload), aad)
s.Key = hex.EncodeToString(key)
s.Payload = hex.EncodeToString(ciphertext)
s.Status = SecretStatusAES256GCM
s.Status = sdkkms.SecretStatusAES256GCM
return nil
default:
return ErrWrongSecretStatus
@ -85,7 +87,7 @@ func (s *builtinSecret) Encrypt() error {
func (s *builtinSecret) Decrypt() error {
switch s.Status {
case SecretStatusAES256GCM:
case sdkkms.SecretStatusAES256GCM:
encrypted, err := hex.DecodeString(s.Payload)
if err != nil {
return err
@ -115,7 +117,7 @@ func (s *builtinSecret) Decrypt() error {
if err != nil {
return err
}
s.Status = SecretStatusPlain
s.Status = sdkkms.SecretStatusPlain
s.Payload = string(plaintext)
s.Key = ""
s.AdditionalData = ""

417
kms/kms.go Normal file
View file

@ -0,0 +1,417 @@
// Package kms provides Key Management Services support
package kms
import (
"encoding/json"
"errors"
"os"
"strings"
"sync"
"github.com/drakkan/sftpgo/v2/logger"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
)
// SecretProvider defines the interface for a KMS secrets provider
type SecretProvider interface {
Name() string
Encrypt() error
Decrypt() error
IsEncrypted() bool
GetStatus() sdkkms.SecretStatus
GetPayload() string
GetKey() string
GetAdditionalData() string
GetMode() int
SetKey(string)
SetAdditionalData(string)
SetStatus(sdkkms.SecretStatus)
Clone() SecretProvider
}
const (
logSender = "kms"
)
// Configuration defines the KMS configuration
type Configuration struct {
Secrets Secrets `json:"secrets" mapstructure:"secrets"`
}
// Secrets define the KMS configuration for encryption/decryption
type Secrets struct {
URL string `json:"url" mapstructure:"url"`
MasterKeyPath string `json:"master_key_path" mapstructure:"master_key_path"`
MasterKeyString string `json:"master_key" mapstructure:"master_key"`
masterKey string
}
type registeredSecretProvider struct {
encryptedStatus sdkkms.SecretStatus
newFn func(base BaseSecret, url, masterKey string) SecretProvider
}
var (
// ErrWrongSecretStatus defines the error to return if the secret status is not appropriate
// for the request operation
ErrWrongSecretStatus = errors.New("wrong secret status")
// ErrInvalidSecret defines the error to return if a secret is not valid
ErrInvalidSecret = errors.New("invalid secret")
validSecretStatuses = []string{sdkkms.SecretStatusPlain, sdkkms.SecretStatusAES256GCM, sdkkms.SecretStatusSecretBox,
sdkkms.SecretStatusVaultTransit, sdkkms.SecretStatusAWS, sdkkms.SecretStatusGCP, sdkkms.SecretStatusRedacted}
config Configuration
secretProviders = make(map[string]registeredSecretProvider)
)
// RegisterSecretProvider register a new secret provider
func RegisterSecretProvider(scheme string, encryptedStatus sdkkms.SecretStatus,
fn func(base BaseSecret, url, masterKey string) SecretProvider,
) {
secretProviders[scheme] = registeredSecretProvider{
encryptedStatus: encryptedStatus,
newFn: fn,
}
}
// NewSecret builds a new Secret using the provided arguments
func NewSecret(status sdkkms.SecretStatus, payload, key, data string) *Secret {
return config.newSecret(status, payload, key, data)
}
// NewEmptySecret returns an empty secret
func NewEmptySecret() *Secret {
return NewSecret("", "", "", "")
}
// NewPlainSecret stores the give payload in a plain text secret
func NewPlainSecret(payload string) *Secret {
return NewSecret(sdkkms.SecretStatusPlain, payload, "", "")
}
// Initialize configures the KMS support
func (c *Configuration) Initialize() error {
if c.Secrets.MasterKeyString != "" {
c.Secrets.masterKey = c.Secrets.MasterKeyString
}
if c.Secrets.masterKey == "" && c.Secrets.MasterKeyPath != "" {
mKey, err := os.ReadFile(c.Secrets.MasterKeyPath)
if err != nil {
return err
}
c.Secrets.masterKey = strings.TrimSpace(string(mKey))
}
config = *c
if config.Secrets.URL == "" {
config.Secrets.URL = sdkkms.SchemeLocal + "://"
}
for k, v := range secretProviders {
logger.Info(logSender, "", "secret provider registered for scheme: %#v, encrypted status: %#v",
k, v.encryptedStatus)
}
return nil
}
func (c *Configuration) newSecret(status sdkkms.SecretStatus, payload, key, data string) *Secret {
base := BaseSecret{
Status: status,
Key: key,
Payload: payload,
AdditionalData: data,
}
return &Secret{
provider: c.getSecretProvider(base),
}
}
func (c *Configuration) getSecretProvider(base BaseSecret) SecretProvider {
for k, v := range secretProviders {
if strings.HasPrefix(c.Secrets.URL, k) {
return v.newFn(base, c.Secrets.URL, c.Secrets.masterKey)
}
}
logger.Warn(logSender, "", "no secret provider registered for URL %v, fallback to local provider", c.Secrets.URL)
return NewLocalSecret(base, c.Secrets.URL, c.Secrets.masterKey)
}
// Secret defines the struct used to store confidential data
type Secret struct {
sync.RWMutex
provider SecretProvider
}
// MarshalJSON return the JSON encoding of the Secret object
func (s *Secret) MarshalJSON() ([]byte, error) {
s.RLock()
defer s.RUnlock()
return json.Marshal(&BaseSecret{
Status: s.provider.GetStatus(),
Payload: s.provider.GetPayload(),
Key: s.provider.GetKey(),
AdditionalData: s.provider.GetAdditionalData(),
Mode: s.provider.GetMode(),
})
}
// UnmarshalJSON parses the JSON-encoded data and stores the result
// in the Secret object
func (s *Secret) UnmarshalJSON(data []byte) error {
s.Lock()
defer s.Unlock()
baseSecret := BaseSecret{}
err := json.Unmarshal(data, &baseSecret)
if err != nil {
return err
}
if baseSecret.isEmpty() {
s.provider = config.getSecretProvider(baseSecret)
return nil
}
if baseSecret.Status == sdkkms.SecretStatusPlain || baseSecret.Status == sdkkms.SecretStatusRedacted {
s.provider = config.getSecretProvider(baseSecret)
return nil
}
for _, v := range secretProviders {
if v.encryptedStatus == baseSecret.Status {
s.provider = v.newFn(baseSecret, config.Secrets.URL, config.Secrets.masterKey)
return nil
}
}
logger.Error(logSender, "", "no provider registered for status %#v", baseSecret.Status)
return ErrInvalidSecret
}
// IsEqual returns true if all the secrets fields are equal
func (s *Secret) IsEqual(other *Secret) bool {
if s.GetStatus() != other.GetStatus() {
return false
}
if s.GetPayload() != other.GetPayload() {
return false
}
if s.GetKey() != other.GetKey() {
return false
}
if s.GetAdditionalData() != other.GetAdditionalData() {
return false
}
if s.GetMode() != other.GetMode() {
return false
}
return true
}
// Clone returns a copy of the secret object
func (s *Secret) Clone() *Secret {
s.RLock()
defer s.RUnlock()
return &Secret{
provider: s.provider.Clone(),
}
}
// IsEncrypted returns true if the secret is encrypted
// This isn't a pointer receiver because we don't want to pass
// a pointer to html template
func (s *Secret) IsEncrypted() bool {
s.RLock()
defer s.RUnlock()
return s.provider.IsEncrypted()
}
// IsPlain returns true if the secret is in plain text
func (s *Secret) IsPlain() bool {
s.RLock()
defer s.RUnlock()
return s.provider.GetStatus() == sdkkms.SecretStatusPlain
}
// IsNotPlainAndNotEmpty returns true if the secret is not plain and not empty.
// This is an utility method, we update the secret for an existing user
// if it is empty or plain
func (s *Secret) IsNotPlainAndNotEmpty() bool {
s.RLock()
defer s.RUnlock()
return !s.IsPlain() && !s.IsEmpty()
}
// IsRedacted returns true if the secret is redacted
func (s *Secret) IsRedacted() bool {
s.RLock()
defer s.RUnlock()
return s.provider.GetStatus() == sdkkms.SecretStatusRedacted
}
// GetPayload returns the secret payload
func (s *Secret) GetPayload() string {
s.RLock()
defer s.RUnlock()
return s.provider.GetPayload()
}
// GetAdditionalData returns the secret additional data
func (s *Secret) GetAdditionalData() string {
s.RLock()
defer s.RUnlock()
return s.provider.GetAdditionalData()
}
// GetStatus returns the secret status
func (s *Secret) GetStatus() sdkkms.SecretStatus {
s.RLock()
defer s.RUnlock()
return s.provider.GetStatus()
}
// GetKey returns the secret key
func (s *Secret) GetKey() string {
s.RLock()
defer s.RUnlock()
return s.provider.GetKey()
}
// GetMode returns the secret mode
func (s *Secret) GetMode() int {
s.RLock()
defer s.RUnlock()
return s.provider.GetMode()
}
// SetAdditionalData sets the given additional data
func (s *Secret) SetAdditionalData(value string) {
s.Lock()
defer s.Unlock()
s.provider.SetAdditionalData(value)
}
// SetStatus sets the status for this secret
func (s *Secret) SetStatus(value sdkkms.SecretStatus) {
s.Lock()
defer s.Unlock()
s.provider.SetStatus(value)
}
// SetKey sets the key for this secret
func (s *Secret) SetKey(value string) {
s.Lock()
defer s.Unlock()
s.provider.SetKey(value)
}
// IsEmpty returns true if all fields are empty
func (s *Secret) IsEmpty() bool {
s.RLock()
defer s.RUnlock()
if s.provider.GetStatus() != "" {
return false
}
if s.provider.GetPayload() != "" {
return false
}
if s.provider.GetKey() != "" {
return false
}
if s.provider.GetAdditionalData() != "" {
return false
}
return true
}
// IsValid returns true if the secret is not empty and valid
func (s *Secret) IsValid() bool {
s.RLock()
defer s.RUnlock()
if !s.IsValidInput() {
return false
}
switch s.provider.GetStatus() {
case sdkkms.SecretStatusAES256GCM, sdkkms.SecretStatusSecretBox:
if len(s.provider.GetKey()) != 64 {
return false
}
case sdkkms.SecretStatusAWS, sdkkms.SecretStatusGCP, sdkkms.SecretStatusVaultTransit:
key := s.provider.GetKey()
if key != "" && len(key) != 64 {
return false
}
}
return true
}
// IsValidInput returns true if the secret is a valid user input
func (s *Secret) IsValidInput() bool {
s.RLock()
defer s.RUnlock()
if !isSecretStatusValid(s.provider.GetStatus()) {
return false
}
if s.provider.GetPayload() == "" {
return false
}
return true
}
// Hide hides info to decrypt data
func (s *Secret) Hide() {
s.Lock()
defer s.Unlock()
s.provider.SetKey("")
s.provider.SetAdditionalData("")
}
// Encrypt encrypts a plain text Secret object
func (s *Secret) Encrypt() error {
s.Lock()
defer s.Unlock()
return s.provider.Encrypt()
}
// Decrypt decrypts a Secret object
func (s *Secret) Decrypt() error {
s.Lock()
defer s.Unlock()
return s.provider.Decrypt()
}
// TryDecrypt decrypts a Secret object if encrypted.
// It returns a nil error if the object is not encrypted
func (s *Secret) TryDecrypt() error {
s.Lock()
defer s.Unlock()
if s.provider.IsEncrypted() {
return s.provider.Decrypt()
}
return nil
}
func isSecretStatusValid(status string) bool {
for idx := range validSecretStatuses {
if validSecretStatuses[idx] == status {
return true
}
}
return false
}

View file

@ -9,10 +9,12 @@ import (
"gocloud.dev/secrets/localsecrets"
"golang.org/x/crypto/hkdf"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
)
func init() {
RegisterSecretProvider(SchemeLocal, SecretStatusSecretBox, NewLocalSecret)
RegisterSecretProvider(sdkkms.SchemeLocal, sdkkms.SecretStatusSecretBox, NewLocalSecret)
}
type localSecret struct {
@ -33,11 +35,11 @@ func (s *localSecret) Name() string {
}
func (s *localSecret) IsEncrypted() bool {
return s.Status == SecretStatusSecretBox
return s.Status == sdkkms.SecretStatusSecretBox
}
func (s *localSecret) Encrypt() error {
if s.Status != SecretStatusPlain {
if s.Status != sdkkms.SecretStatusPlain {
return ErrWrongSecretStatus
}
if s.Payload == "" {
@ -60,7 +62,7 @@ func (s *localSecret) Encrypt() error {
}
s.Key = hex.EncodeToString(secretKey[:])
s.Payload = base64.StdEncoding.EncodeToString(ciphertext)
s.Status = SecretStatusSecretBox
s.Status = sdkkms.SecretStatusSecretBox
s.Mode = s.getEncryptionMode()
return nil
}
@ -88,7 +90,7 @@ func (s *localSecret) Decrypt() error {
if err != nil {
return err
}
s.Status = SecretStatusPlain
s.Status = sdkkms.SecretStatusPlain
s.Payload = string(plaintext)
s.Key = ""
s.AdditionalData = ""

View file

@ -18,8 +18,6 @@ import (
ftpserverlog "github.com/fclairamb/go-log"
"github.com/rs/zerolog"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
sdklogger "github.com/drakkan/sftpgo/v2/sdk/logger"
)
const (
@ -43,22 +41,6 @@ var (
rollingLogger *lumberjack.Logger
)
type logWrapper struct{}
// Log logs at the specified level for the specified sender
func (l *logWrapper) Log(level int, sender, format string, v ...interface{}) {
switch level {
case 1:
Log(LevelInfo, sender, "", format, v...)
case 2:
Log(LevelWarn, sender, "", format, v...)
case 3:
Log(LevelError, sender, "", format, v...)
default:
Log(LevelDebug, sender, "", format, v...)
}
}
// StdLoggerWrapper is a wrapper for standard logger compatibility
type StdLoggerWrapper struct {
Sender string
@ -194,7 +176,6 @@ func InitLogger(logFilePath string, logMaxSize int, logMaxBackups int, logMaxAge
consoleLogger = zerolog.Nop()
}
logger = logger.Level(level)
sdklogger.SetLogger(&logWrapper{})
}
// InitStdErrLogger configures the logger to write to stderr
@ -203,7 +184,6 @@ func InitStdErrLogger(level zerolog.Level) {
output: os.Stderr,
}).Level(level)
consoleLogger = zerolog.Nop()
sdklogger.SetLogger(&logWrapper{})
}
// DisableLogger disable the main logger.
@ -211,7 +191,6 @@ func InitStdErrLogger(level zerolog.Level) {
func DisableLogger() {
logger = zerolog.Nop()
rollingLogger = nil
sdklogger.DisableLogger()
}
// EnableConsoleLogger enables the console logger

View file

@ -9,16 +9,17 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms"
"github.com/drakkan/sftpgo/v2/util"
)
var (
validKMSSchemes = []string{kms.SchemeAWS, kms.SchemeGCP, kms.SchemeVaultTransit, kms.SchemeAzureKeyVault}
validKMSEncryptedStatuses = []string{kms.SecretStatusVaultTransit, kms.SecretStatusAWS, kms.SecretStatusGCP,
kms.SecretStatusAzureKeyVault}
validKMSSchemes = []string{sdkkms.SchemeAWS, sdkkms.SchemeGCP, sdkkms.SchemeVaultTransit, sdkkms.SchemeAzureKeyVault}
validKMSEncryptedStatuses = []string{sdkkms.SecretStatusVaultTransit, sdkkms.SecretStatusAWS, sdkkms.SecretStatusGCP,
sdkkms.SecretStatusAzureKeyVault}
)
// KMSConfig defines configuration parameters for kms plugins
@ -133,7 +134,7 @@ func (s *kmsPluginSecretProvider) IsEncrypted() bool {
}
func (s *kmsPluginSecretProvider) Encrypt() error {
if s.Status != kms.SecretStatusPlain {
if s.Status != sdkkms.SecretStatusPlain {
return kms.ErrWrongSecretStatus
}
if s.Payload == "" {
@ -160,7 +161,7 @@ func (s *kmsPluginSecretProvider) Decrypt() error {
if err != nil {
return err
}
s.Status = kms.SecretStatusPlain
s.Status = sdkkms.SecretStatusPlain
s.Payload = payload
s.Key = ""
s.AdditionalData = ""

View file

@ -11,8 +11,8 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sdk/plugin/auth"
"github.com/drakkan/sftpgo/v2/sdk/plugin/eventsearcher"
kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms"
@ -135,7 +135,7 @@ func Initialize(configs []Config, logVerbose bool) error {
kmsID++
kms.RegisterSecretProvider(config.KMSOptions.Scheme, config.KMSOptions.EncryptedStatus,
Handler.Configs[idx].newKMSPluginSecretProvider)
logger.Debug(logSender, "", "registered secret provider for scheme: %v, encrypted status: %v",
logger.Info(logSender, "", "registered secret provider for scheme: %v, encrypted status: %v",
config.KMSOptions.Scheme, config.KMSOptions.EncryptedStatus)
case auth.PluginName:
plugin, err := newAuthPlugin(config)

View file

@ -84,8 +84,8 @@ func ListProviders() []FilesystemProvider {
}
}
// S3FsConfig defines the configuration for S3 based filesystem
type S3FsConfig struct {
// BaseS3FsConfig defines the base configuration for S3 based filesystems
type BaseS3FsConfig struct {
Bucket string `json:"bucket,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified then the SFTP user will only see objects that starts
@ -96,7 +96,6 @@ type S3FsConfig struct {
KeyPrefix string `json:"key_prefix,omitempty"`
Region string `json:"region,omitempty"`
AccessKey string `json:"access_key,omitempty"`
AccessSecret *kms.Secret `json:"access_secret,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
StorageClass string `json:"storage_class,omitempty"`
// The canned ACL to apply to uploaded objects. Leave empty to use the default ACL.
@ -129,8 +128,14 @@ type S3FsConfig struct {
ForcePathStyle bool `json:"force_path_style,omitempty"`
}
// GCSFsConfig defines the configuration for Google Cloud Storage based filesystem
type GCSFsConfig struct {
// S3FsConfig defines the base configuration for S3 based filesystems
type S3FsConfig struct {
BaseS3FsConfig
AccessSecret kms.BaseSecret `json:"access_secret,omitempty"`
}
// BaseGCSFsConfig defines the base configuration for Google Cloud Storage based filesystems
type BaseGCSFsConfig struct {
Bucket string `json:"bucket,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified then the SFTP user will only see objects that starts
@ -140,7 +145,6 @@ type GCSFsConfig struct {
// If empty the whole bucket contents will be available
KeyPrefix string `json:"key_prefix,omitempty"`
CredentialFile string `json:"-"`
Credentials *kms.Secret `json:"credentials,omitempty"`
// 0 explicit, 1 automatic
AutomaticCredentials int `json:"automatic_credentials,omitempty"`
StorageClass string `json:"storage_class,omitempty"`
@ -150,20 +154,21 @@ type GCSFsConfig struct {
ACL string `json:"acl,omitempty"`
}
// AzBlobFsConfig defines the configuration for Azure Blob Storage based filesystem
type AzBlobFsConfig struct {
// GCSFsConfig defines the configuration for Google Cloud Storage based filesystems
type GCSFsConfig struct {
BaseGCSFsConfig
Credentials kms.BaseSecret `json:"credentials,omitempty"`
}
// BaseAzBlobFsConfig defines the base configuration for Azure Blob Storage based filesystem
type BaseAzBlobFsConfig struct {
Container string `json:"container,omitempty"`
// Storage Account Name, leave blank to use SAS URL
AccountName string `json:"account_name,omitempty"`
// Storage Account Key leave blank to use SAS URL.
// The access key is stored encrypted based on the kms configuration
AccountKey *kms.Secret `json:"account_key,omitempty"`
// Optional endpoint. Default is "blob.core.windows.net".
// If you use the emulator the endpoint must include the protocol,
// for example "http://127.0.0.1:10000"
Endpoint string `json:"endpoint,omitempty"`
// Shared access signature URL, leave blank if using account/key
SASURL *kms.Secret `json:"sas_url,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified then the SFTPGo user will only see objects that starts
// with this prefix and so you can restrict access to a specific
@ -187,17 +192,25 @@ type AzBlobFsConfig struct {
AccessTier string `json:"access_tier,omitempty"`
}
// CryptFsConfig defines the configuration to store local files as encrypted
type CryptFsConfig struct {
Passphrase *kms.Secret `json:"passphrase,omitempty"`
// AzBlobFsConfig defines the configuration for Azure Blob Storage based filesystem
type AzBlobFsConfig struct {
BaseAzBlobFsConfig
// Storage Account Key leave blank to use SAS URL.
// The access key is stored encrypted based on the kms configuration
AccountKey kms.BaseSecret `json:"account_key,omitempty"`
// Shared access signature URL, leave blank if using account/key
SASURL kms.BaseSecret `json:"sas_url,omitempty"`
}
// SFTPFsConfig defines the configuration for SFTP based filesystem
type SFTPFsConfig struct {
// CryptFsConfig defines the configuration to store local files as encrypted
type CryptFsConfig struct {
Passphrase kms.BaseSecret `json:"passphrase,omitempty"`
}
// BaseSFTPFsConfig defines the base configuration for SFTP based filesystem
type BaseSFTPFsConfig struct {
Endpoint string `json:"endpoint,omitempty"`
Username string `json:"username,omitempty"`
Password *kms.Secret `json:"password,omitempty"`
PrivateKey *kms.Secret `json:"private_key,omitempty"`
Fingerprints []string `json:"fingerprints,omitempty"`
// Prefix is the path prefix to strip from SFTP resource paths.
Prefix string `json:"prefix,omitempty"`
@ -213,6 +226,13 @@ type SFTPFsConfig struct {
BufferSize int64 `json:"buffer_size,omitempty"`
}
// SFTPFsConfig defines the configuration for SFTP based filesystem
type SFTPFsConfig struct {
BaseSFTPFsConfig
Password kms.BaseSecret `json:"password,omitempty"`
PrivateKey kms.BaseSecret `json:"private_key,omitempty"`
}
// Filesystem defines filesystem details
type Filesystem struct {
Provider FilesystemProvider `json:"provider"`

View file

@ -1,37 +1,6 @@
// Package kms provides Key Management Services support
package kms
import (
"encoding/json"
"errors"
"os"
"strings"
"sync"
"github.com/drakkan/sftpgo/v2/sdk/logger"
)
// SecretProvider defines the interface for a KMS secrets provider
type SecretProvider interface {
Name() string
Encrypt() error
Decrypt() error
IsEncrypted() bool
GetStatus() SecretStatus
GetPayload() string
GetKey() string
GetAdditionalData() string
GetMode() int
SetKey(string)
SetAdditionalData(string)
SetStatus(SecretStatus)
Clone() SecretProvider
}
const (
logSender = "kms"
)
// SecretStatus defines the statuses of a Secret object
type SecretStatus = string
@ -70,384 +39,12 @@ const (
SchemeAzureKeyVault Scheme = "azurekeyvault"
)
// Configuration defines the KMS configuration
type Configuration struct {
Secrets Secrets `json:"secrets" mapstructure:"secrets"`
}
// Secrets define the KMS configuration for encryption/decryption
type Secrets struct {
URL string `json:"url" mapstructure:"url"`
MasterKeyPath string `json:"master_key_path" mapstructure:"master_key_path"`
MasterKeyString string `json:"master_key" mapstructure:"master_key"`
masterKey string
}
type registeredSecretProvider struct {
encryptedStatus SecretStatus
newFn func(base BaseSecret, url, masterKey string) SecretProvider
}
var (
// ErrWrongSecretStatus defines the error to return if the secret status is not appropriate
// for the request operation
ErrWrongSecretStatus = errors.New("wrong secret status")
// ErrInvalidSecret defines the error to return if a secret is not valid
ErrInvalidSecret = errors.New("invalid secret")
validSecretStatuses = []string{SecretStatusPlain, SecretStatusAES256GCM, SecretStatusSecretBox,
SecretStatusVaultTransit, SecretStatusAWS, SecretStatusGCP, SecretStatusRedacted}
config Configuration
secretProviders = make(map[string]registeredSecretProvider)
)
// RegisterSecretProvider register a new secret provider
func RegisterSecretProvider(scheme string, encryptedStatus SecretStatus, fn func(base BaseSecret, url, masterKey string) SecretProvider) {
secretProviders[scheme] = registeredSecretProvider{
encryptedStatus: encryptedStatus,
newFn: fn,
}
}
// NewSecret builds a new Secret using the provided arguments
func NewSecret(status SecretStatus, payload, key, data string) *Secret {
return config.newSecret(status, payload, key, data)
}
// NewEmptySecret returns an empty secret
func NewEmptySecret() *Secret {
return NewSecret("", "", "", "")
}
// NewPlainSecret stores the give payload in a plain text secret
func NewPlainSecret(payload string) *Secret {
return NewSecret(SecretStatusPlain, payload, "", "")
}
// Initialize configures the KMS support
func (c *Configuration) Initialize() error {
if c.Secrets.MasterKeyString != "" {
c.Secrets.masterKey = c.Secrets.MasterKeyString
}
if c.Secrets.masterKey == "" && c.Secrets.MasterKeyPath != "" {
mKey, err := os.ReadFile(c.Secrets.MasterKeyPath)
if err != nil {
return err
}
c.Secrets.masterKey = strings.TrimSpace(string(mKey))
}
config = *c
if config.Secrets.URL == "" {
config.Secrets.URL = SchemeLocal + "://"
}
for k, v := range secretProviders {
logger.Info(logSender, "secret provider registered for scheme: %#v, encrypted status: %#v",
k, v.encryptedStatus)
}
return nil
}
func (c *Configuration) newSecret(status SecretStatus, payload, key, data string) *Secret {
base := BaseSecret{
Status: status,
Key: key,
Payload: payload,
AdditionalData: data,
}
return &Secret{
provider: c.getSecretProvider(base),
}
}
func (c *Configuration) getSecretProvider(base BaseSecret) SecretProvider {
for k, v := range secretProviders {
if strings.HasPrefix(c.Secrets.URL, k) {
return v.newFn(base, c.Secrets.URL, c.Secrets.masterKey)
}
}
// we assume that SchemeLocal is always registered
logger.Warn(logSender, "no secret provider registered for URL %v, fallback to local provider", c.Secrets.URL)
return NewLocalSecret(base, c.Secrets.URL, c.Secrets.masterKey)
}
// Secret defines the struct used to store confidential data
type Secret struct {
sync.RWMutex
provider SecretProvider
}
// MarshalJSON return the JSON encoding of the Secret object
func (s *Secret) MarshalJSON() ([]byte, error) {
s.RLock()
defer s.RUnlock()
return json.Marshal(&BaseSecret{
Status: s.provider.GetStatus(),
Payload: s.provider.GetPayload(),
Key: s.provider.GetKey(),
AdditionalData: s.provider.GetAdditionalData(),
Mode: s.provider.GetMode(),
})
}
// UnmarshalJSON parses the JSON-encoded data and stores the result
// in the Secret object
func (s *Secret) UnmarshalJSON(data []byte) error {
s.Lock()
defer s.Unlock()
baseSecret := BaseSecret{}
err := json.Unmarshal(data, &baseSecret)
if err != nil {
return err
}
if baseSecret.isEmpty() {
s.provider = config.getSecretProvider(baseSecret)
return nil
}
if baseSecret.Status == SecretStatusPlain || baseSecret.Status == SecretStatusRedacted {
s.provider = config.getSecretProvider(baseSecret)
return nil
}
for _, v := range secretProviders {
if v.encryptedStatus == baseSecret.Status {
s.provider = v.newFn(baseSecret, config.Secrets.URL, config.Secrets.masterKey)
return nil
}
}
logger.Error(logSender, "no provider registered for status %#v", baseSecret.Status)
return ErrInvalidSecret
}
// IsEqual returns true if all the secrets fields are equal
func (s *Secret) IsEqual(other *Secret) bool {
if s.GetStatus() != other.GetStatus() {
return false
}
if s.GetPayload() != other.GetPayload() {
return false
}
if s.GetKey() != other.GetKey() {
return false
}
if s.GetAdditionalData() != other.GetAdditionalData() {
return false
}
if s.GetMode() != other.GetMode() {
return false
}
return true
}
// Clone returns a copy of the secret object
func (s *Secret) Clone() *Secret {
s.RLock()
defer s.RUnlock()
return &Secret{
provider: s.provider.Clone(),
}
}
// IsEncrypted returns true if the secret is encrypted
// This isn't a pointer receiver because we don't want to pass
// a pointer to html template
func (s *Secret) IsEncrypted() bool {
s.RLock()
defer s.RUnlock()
return s.provider.IsEncrypted()
}
// IsPlain returns true if the secret is in plain text
func (s *Secret) IsPlain() bool {
s.RLock()
defer s.RUnlock()
return s.provider.GetStatus() == SecretStatusPlain
}
// IsNotPlainAndNotEmpty returns true if the secret is not plain and not empty.
// This is an utility method, we update the secret for an existing user
// if it is empty or plain
func (s *Secret) IsNotPlainAndNotEmpty() bool {
s.RLock()
defer s.RUnlock()
return !s.IsPlain() && !s.IsEmpty()
}
// IsRedacted returns true if the secret is redacted
func (s *Secret) IsRedacted() bool {
s.RLock()
defer s.RUnlock()
return s.provider.GetStatus() == SecretStatusRedacted
}
// GetPayload returns the secret payload
func (s *Secret) GetPayload() string {
s.RLock()
defer s.RUnlock()
return s.provider.GetPayload()
}
// GetAdditionalData returns the secret additional data
func (s *Secret) GetAdditionalData() string {
s.RLock()
defer s.RUnlock()
return s.provider.GetAdditionalData()
}
// GetStatus returns the secret status
func (s *Secret) GetStatus() SecretStatus {
s.RLock()
defer s.RUnlock()
return s.provider.GetStatus()
}
// GetKey returns the secret key
func (s *Secret) GetKey() string {
s.RLock()
defer s.RUnlock()
return s.provider.GetKey()
}
// GetMode returns the secret mode
func (s *Secret) GetMode() int {
s.RLock()
defer s.RUnlock()
return s.provider.GetMode()
}
// SetAdditionalData sets the given additional data
func (s *Secret) SetAdditionalData(value string) {
s.Lock()
defer s.Unlock()
s.provider.SetAdditionalData(value)
}
// SetStatus sets the status for this secret
func (s *Secret) SetStatus(value SecretStatus) {
s.Lock()
defer s.Unlock()
s.provider.SetStatus(value)
}
// SetKey sets the key for this secret
func (s *Secret) SetKey(value string) {
s.Lock()
defer s.Unlock()
s.provider.SetKey(value)
}
// IsEmpty returns true if all fields are empty
func (s *Secret) IsEmpty() bool {
s.RLock()
defer s.RUnlock()
if s.provider.GetStatus() != "" {
return false
}
if s.provider.GetPayload() != "" {
return false
}
if s.provider.GetKey() != "" {
return false
}
if s.provider.GetAdditionalData() != "" {
return false
}
return true
}
// IsValid returns true if the secret is not empty and valid
func (s *Secret) IsValid() bool {
s.RLock()
defer s.RUnlock()
if !s.IsValidInput() {
return false
}
switch s.provider.GetStatus() {
case SecretStatusAES256GCM, SecretStatusSecretBox:
if len(s.provider.GetKey()) != 64 {
return false
}
case SecretStatusAWS, SecretStatusGCP, SecretStatusVaultTransit:
key := s.provider.GetKey()
if key != "" && len(key) != 64 {
return false
}
}
return true
}
// IsValidInput returns true if the secret is a valid user input
func (s *Secret) IsValidInput() bool {
s.RLock()
defer s.RUnlock()
if !isSecretStatusValid(s.provider.GetStatus()) {
return false
}
if s.provider.GetPayload() == "" {
return false
}
return true
}
// Hide hides info to decrypt data
func (s *Secret) Hide() {
s.Lock()
defer s.Unlock()
s.provider.SetKey("")
s.provider.SetAdditionalData("")
}
// Encrypt encrypts a plain text Secret object
func (s *Secret) Encrypt() error {
s.Lock()
defer s.Unlock()
return s.provider.Encrypt()
}
// Decrypt decrypts a Secret object
func (s *Secret) Decrypt() error {
s.Lock()
defer s.Unlock()
return s.provider.Decrypt()
}
// TryDecrypt decrypts a Secret object if encrypted.
// It returns a nil error if the object is not encrypted
func (s *Secret) TryDecrypt() error {
s.Lock()
defer s.Unlock()
if s.provider.IsEncrypted() {
return s.provider.Decrypt()
}
return nil
}
func isSecretStatusValid(status string) bool {
for idx := range validSecretStatuses {
if validSecretStatuses[idx] == status {
return true
}
}
return false
// BaseSecret defines the base struct shared among all the secret providers
type BaseSecret struct {
Status SecretStatus `json:"status,omitempty"`
Payload string `json:"payload,omitempty"`
Key string `json:"key,omitempty"`
AdditionalData string `json:"additional_data,omitempty"`
// 1 means encrypted using a master key
Mode int `json:"mode,omitempty"`
}

View file

@ -1,57 +0,0 @@
// Package logger provides logging capabilities.
package logger
const (
levelDebug = iota
levelInfo
levelWarn
levelError
)
var (
logger Logger
)
func init() {
DisableLogger()
}
// Logger interface
type Logger interface {
// Log logs at the specified level for the specified sender
Log(level int, sender, format string, v ...interface{})
}
// SetLogger sets the specified logger
func SetLogger(l Logger) {
logger = l
}
// DisableLogger disables logging
func DisableLogger() {
logger = &noLogger{}
}
type noLogger struct{}
func (*noLogger) Log(level int, sender, format string, v ...interface{}) {}
// Debug logs at debug level for the specified sender
func Debug(sender, format string, v ...interface{}) {
logger.Log(levelDebug, sender, format, v...)
}
// Info logs at info level for the specified sender
func Info(sender, format string, v ...interface{}) {
logger.Log(levelInfo, sender, format, v...)
}
// Warn logs at warn level for the specified sender
func Warn(sender, format string, v ...interface{}) {
logger.Log(levelWarn, sender, format, v...)
}
// Error logs at error level for the specified sender
func Error(sender, format string, v ...interface{}) {
logger.Log(levelError, sender, format, v...)
}

View file

@ -100,7 +100,7 @@ type HooksFilter struct {
// RecoveryCode defines a 2FA recovery code
type RecoveryCode struct {
Secret *kms.Secret `json:"secret"`
Secret kms.BaseSecret `json:"secret"`
Used bool `json:"used,omitempty"`
}
@ -108,7 +108,7 @@ type RecoveryCode struct {
type TOTPConfig struct {
Enabled bool `json:"enabled,omitempty"`
ConfigName string `json:"config_name,omitempty"`
Secret *kms.Secret `json:"secret,omitempty"`
Secret kms.BaseSecret `json:"secret,omitempty"`
// TOTP will be required for the specified protocols.
// SSH protocol (SFTP/SCP/SSH commands) will ask for the TOTP passcode if the client uses keyboard interactive
// authentication.
@ -136,9 +136,8 @@ func (l *BandwidthLimit) GetSourcesAsString() string {
return strings.Join(l.Sources, ",")
}
// UserFilters defines additional restrictions for a user
// TODO: rename to UserOptions in v3
type UserFilters struct {
// BaseUserFilters defines additional restrictions for a user
type BaseUserFilters struct {
// only clients connecting from these IP/Mask are allowed.
// IP/Mask must be in CIDR notation as defined in RFC 4632 and RFC 4291
// for example "192.0.2.0/24" or "2001:db8::/32"
@ -175,17 +174,23 @@ type UserFilters struct {
WebClient []string `json:"web_client,omitempty"`
// API key auth allows to impersonate this user with an API key
AllowAPIKeyAuth bool `json:"allow_api_key_auth,omitempty"`
// UserType is an hint for authentication plugins.
// It is ignored when using SFTPGo internal authentication
UserType string `json:"user_type,omitempty"`
// Per-source bandwidth limits
BandwidthLimits []BandwidthLimit `json:"bandwidth_limits,omitempty"`
}
// UserFilters defines additional restrictions for a user
// TODO: rename to UserOptions in v3
type UserFilters struct {
BaseUserFilters
// Time-based one time passwords configuration
TOTPConfig TOTPConfig `json:"totp_config,omitempty"`
// Recovery codes to use if the user loses access to their second factor auth device.
// Each code can only be used once, you should use these codes to login and disable or
// reset 2FA for your account
RecoveryCodes []RecoveryCode `json:"recovery_codes,omitempty"`
// UserType is an hint for authentication plugins.
// It is ignored when using SFTPGo internal authentication
UserType string `json:"user_type,omitempty"`
// Per-source bandwidth limits
BandwidthLimits []BandwidthLimit `json:"bandwidth_limits,omitempty"`
}
// BaseUser defines the shared user fields
@ -239,8 +244,6 @@ type BaseUser struct {
CreatedAt int64 `json:"created_at"`
// last update time as unix timestamp in milliseconds
UpdatedAt int64 `json:"updated_at"`
// Additional restrictions
Filters UserFilters `json:"filters"`
// optional description, for example full name
Description string `json:"description,omitempty"`
// free form text field for external systems
@ -250,6 +253,8 @@ type BaseUser struct {
// User defines a SFTPGo user
type User struct {
BaseUser
// Additional restrictions
Filters UserFilters `json:"filters"`
// Mapping between virtual paths and virtual folders
VirtualFolders []VirtualFolder `json:"virtual_folders,omitempty"`
// Filesystem configuration details

View file

@ -17,9 +17,9 @@ import (
"github.com/drakkan/sftpgo/v2/config"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/ftpd"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/version"

View file

@ -15,8 +15,8 @@ import (
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/vfs"
)

View file

@ -20,8 +20,8 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)
@ -667,7 +667,7 @@ func TestSSHCommandsRemoteFs(t *testing.T) {
user.FsConfig = vfs.Filesystem{
Provider: sdk.S3FilesystemProvider,
S3Config: vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: "s3bucket",
Endpoint: "endpoint",
Region: "eu-west-1",
@ -1753,11 +1753,9 @@ func TestTransferFailingReader(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret("crypt secret"),
},
},
},
}
user.Permissions = make(map[string][]string)
user.Permissions["/"] = []string{dataprovider.PermAny}

View file

@ -42,10 +42,11 @@ import (
"github.com/drakkan/sftpgo/v2/config"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/mfa"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
@ -1999,7 +2000,7 @@ func TestLoginWithDatabaseCredentials(t *testing.T) {
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus())
assert.NotEmpty(t, user.FsConfig.GCSConfig.Credentials.GetPayload())
assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetAdditionalData())
assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetKey())
@ -2283,7 +2284,7 @@ func TestInteractiveLoginWithPasscode(t *testing.T) {
configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -2332,7 +2333,7 @@ func TestInteractiveLoginWithPasscode(t *testing.T) {
configName, _, secret, _, err = mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
@ -2521,14 +2522,14 @@ func TestPreLoginHookPreserveMFAConfig(t *testing.T) {
configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
Protocols: []string{common.ProtocolSSH},
}
for i := 0; i < 12; i++ {
user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, sdk.RecoveryCode{
user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, dataprovider.RecoveryCode{
Secret: kms.NewPlainSecret(fmt.Sprintf("RC-%v", strings.ToUpper(util.GenerateUniqueID()))),
})
}
@ -2548,7 +2549,7 @@ func TestPreLoginHookPreserveMFAConfig(t *testing.T) {
assert.True(t, user.Filters.TOTPConfig.Enabled)
assert.Equal(t, configName, user.Filters.TOTPConfig.ConfigName)
assert.Equal(t, []string{common.ProtocolSSH}, user.Filters.TOTPConfig.Protocols)
assert.Equal(t, kms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
err = os.WriteFile(extAuthPath, getExitCodeScriptContent(0), os.ModePerm)
assert.NoError(t, err)
@ -2566,7 +2567,7 @@ func TestPreLoginHookPreserveMFAConfig(t *testing.T) {
assert.True(t, user.Filters.TOTPConfig.Enabled)
assert.Equal(t, configName, user.Filters.TOTPConfig.ConfigName)
assert.Equal(t, []string{common.ProtocolSSH}, user.Filters.TOTPConfig.Protocols)
assert.Equal(t, kms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
@ -3463,14 +3464,14 @@ func TestExternalAuthPreserveMFAConfig(t *testing.T) {
configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.TOTPConfig = sdk.TOTPConfig{
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: true,
ConfigName: configName,
Secret: kms.NewPlainSecret(secret),
Protocols: []string{common.ProtocolSSH},
}
for i := 0; i < 12; i++ {
user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, sdk.RecoveryCode{
user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, dataprovider.RecoveryCode{
Secret: kms.NewPlainSecret(fmt.Sprintf("RC-%v", strings.ToUpper(util.GenerateUniqueID()))),
})
}
@ -3490,7 +3491,7 @@ func TestExternalAuthPreserveMFAConfig(t *testing.T) {
assert.True(t, user.Filters.TOTPConfig.Enabled)
assert.Equal(t, configName, user.Filters.TOTPConfig.ConfigName)
assert.Equal(t, []string{common.ProtocolSSH}, user.Filters.TOTPConfig.Protocols)
assert.Equal(t, kms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, true, ""), os.ModePerm)
assert.NoError(t, err)
@ -3508,7 +3509,7 @@ func TestExternalAuthPreserveMFAConfig(t *testing.T) {
assert.True(t, user.Filters.TOTPConfig.Enabled)
assert.Equal(t, configName, user.Filters.TOTPConfig.ConfigName)
assert.Equal(t, []string{common.ProtocolSSH}, user.Filters.TOTPConfig.Protocols)
assert.Equal(t, kms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
@ -4428,18 +4429,18 @@ func TestSFTPLoopSimple(t *testing.T) {
user1.FsConfig.Provider = sdk.SFTPFilesystemProvider
user2.FsConfig.Provider = sdk.SFTPFilesystemProvider
user1.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user2.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user1.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated)
assert.NoError(t, err, string(resp))
@ -4487,11 +4488,11 @@ func TestSFTPLoopVirtualFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user2.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
},
},
@ -4500,19 +4501,19 @@ func TestSFTPLoopVirtualFolders(t *testing.T) {
user2.FsConfig.Provider = sdk.SFTPFilesystemProvider
user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user1.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user3.FsConfig.Provider = sdk.SFTPFilesystemProvider
user3.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user1.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated)
@ -4546,11 +4547,11 @@ func TestSFTPLoopVirtualFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user3.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
},
},
@ -4602,11 +4603,9 @@ func TestNestedVirtualFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
MappedPath: mappedPathCrypt,
},
VirtualPath: vdirCryptPath,
@ -7222,24 +7221,24 @@ func TestRelativePaths(t *testing.T) {
filesystems := []vfs.Fs{vfs.NewOsFs("", user.GetHomeDir(), "")}
keyPrefix := strings.TrimPrefix(user.GetHomeDir(), "/") + "/"
s3config := vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
KeyPrefix: keyPrefix,
},
}
s3fs, _ := vfs.NewS3Fs("", user.GetHomeDir(), "", s3config)
gcsConfig := vfs.GCSFsConfig{
GCSFsConfig: sdk.GCSFsConfig{
BaseGCSFsConfig: sdk.BaseGCSFsConfig{
KeyPrefix: keyPrefix,
},
}
gcsfs, _ := vfs.NewGCSFs("", user.GetHomeDir(), "", gcsConfig)
sftpconfig := vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: defaultUsername,
Password: kms.NewPlainSecret(defaultPassword),
Prefix: keyPrefix,
},
Password: kms.NewPlainSecret(defaultPassword),
}
sftpfs, _ := vfs.NewSFTPFs("", "", os.TempDir(), []string{user.Username}, sftpconfig)
if runtime.GOOS != osWindows {
@ -7287,7 +7286,7 @@ func TestResolvePaths(t *testing.T) {
filesystems := []vfs.Fs{vfs.NewOsFs("", user.GetHomeDir(), "")}
keyPrefix := strings.TrimPrefix(user.GetHomeDir(), "/") + "/"
s3config := vfs.S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
KeyPrefix: keyPrefix,
Bucket: "bucket",
Region: "us-east-1",
@ -7298,7 +7297,7 @@ func TestResolvePaths(t *testing.T) {
s3fs, err := vfs.NewS3Fs("", user.GetHomeDir(), "", s3config)
assert.NoError(t, err)
gcsConfig := vfs.GCSFsConfig{
GCSFsConfig: sdk.GCSFsConfig{
BaseGCSFsConfig: sdk.BaseGCSFsConfig{
KeyPrefix: keyPrefix,
},
}
@ -7401,8 +7400,10 @@ func TestFilterFilePatterns(t *testing.T) {
AllowedPatterns: []string{"*.jpg", "*.png"},
DeniedPatterns: []string{"*.pdf"},
}
filters := sdk.UserFilters{
filters := dataprovider.UserFilters{
BaseUserFilters: sdk.BaseUserFilters{
FilePatterns: []sdk.PatternsFilter{pattern},
},
}
user.Filters = filters
assert.True(t, user.IsFileAllowed("/test/test.jPg"))
@ -8482,12 +8483,10 @@ func TestSSHRemoveCryptFs(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
},
VirtualPath: vdirPath2,
QuotaFiles: 100,
QuotaSize: 0,
@ -9070,11 +9069,11 @@ func TestSCPNestedFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: baseUser.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
},
},
@ -9089,11 +9088,9 @@ func TestSCPNestedFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
MappedPath: mappedPathCrypt,
},
VirtualPath: vdirCryptPath,

View file

@ -3,8 +3,8 @@ package vfs
import (
"fmt"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
)
@ -228,11 +228,10 @@ func (f *Filesystem) GetACopy() Filesystem {
fs := Filesystem{
Provider: f.Provider,
S3Config: S3FsConfig{
S3FsConfig: sdk.S3FsConfig{
BaseS3FsConfig: sdk.BaseS3FsConfig{
Bucket: f.S3Config.Bucket,
Region: f.S3Config.Region,
AccessKey: f.S3Config.AccessKey,
AccessSecret: f.S3Config.AccessSecret.Clone(),
Endpoint: f.S3Config.Endpoint,
StorageClass: f.S3Config.StorageClass,
ACL: f.S3Config.ACL,
@ -244,47 +243,46 @@ func (f *Filesystem) GetACopy() Filesystem {
DownloadPartMaxTime: f.S3Config.DownloadPartMaxTime,
ForcePathStyle: f.S3Config.ForcePathStyle,
},
AccessSecret: f.S3Config.AccessSecret.Clone(),
},
GCSConfig: GCSFsConfig{
GCSFsConfig: sdk.GCSFsConfig{
BaseGCSFsConfig: sdk.BaseGCSFsConfig{
Bucket: f.GCSConfig.Bucket,
CredentialFile: f.GCSConfig.CredentialFile,
Credentials: f.GCSConfig.Credentials.Clone(),
AutomaticCredentials: f.GCSConfig.AutomaticCredentials,
StorageClass: f.GCSConfig.StorageClass,
ACL: f.GCSConfig.ACL,
KeyPrefix: f.GCSConfig.KeyPrefix,
},
Credentials: f.GCSConfig.Credentials.Clone(),
},
AzBlobConfig: AzBlobFsConfig{
AzBlobFsConfig: sdk.AzBlobFsConfig{
BaseAzBlobFsConfig: sdk.BaseAzBlobFsConfig{
Container: f.AzBlobConfig.Container,
AccountName: f.AzBlobConfig.AccountName,
AccountKey: f.AzBlobConfig.AccountKey.Clone(),
Endpoint: f.AzBlobConfig.Endpoint,
SASURL: f.AzBlobConfig.SASURL.Clone(),
KeyPrefix: f.AzBlobConfig.KeyPrefix,
UploadPartSize: f.AzBlobConfig.UploadPartSize,
UploadConcurrency: f.AzBlobConfig.UploadConcurrency,
UseEmulator: f.AzBlobConfig.UseEmulator,
AccessTier: f.AzBlobConfig.AccessTier,
},
AccountKey: f.AzBlobConfig.AccountKey.Clone(),
SASURL: f.AzBlobConfig.SASURL.Clone(),
},
CryptConfig: CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: f.CryptConfig.Passphrase.Clone(),
},
},
SFTPConfig: SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: f.SFTPConfig.Endpoint,
Username: f.SFTPConfig.Username,
Password: f.SFTPConfig.Password.Clone(),
PrivateKey: f.SFTPConfig.PrivateKey.Clone(),
Prefix: f.SFTPConfig.Prefix,
DisableCouncurrentReads: f.SFTPConfig.DisableCouncurrentReads,
BufferSize: f.SFTPConfig.BufferSize,
},
Password: f.SFTPConfig.Password.Clone(),
PrivateKey: f.SFTPConfig.PrivateKey.Clone(),
},
}
if len(f.SFTPConfig.Fingerprints) > 0 {

View file

@ -23,10 +23,10 @@ import (
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/metric"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/version"
)

View file

@ -19,9 +19,9 @@ import (
"github.com/rs/xid"
"golang.org/x/crypto/ssh"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/version"
)
@ -36,7 +36,9 @@ var ErrSFTPLoop = errors.New("SFTP loop or nested local SFTP folders detected")
// SFTPFsConfig defines the configuration for SFTP based filesystem
type SFTPFsConfig struct {
sdk.SFTPFsConfig
sdk.BaseSFTPFsConfig
Password *kms.Secret `json:"password,omitempty"`
PrivateKey *kms.Secret `json:"private_key,omitempty"`
forbiddenSelfUsernames []string `json:"-"`
}

View file

@ -16,10 +16,10 @@ import (
"github.com/eikenb/pipeat"
"github.com/pkg/sftp"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/plugin"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sdk/plugin/metadata"
"github.com/drakkan/sftpgo/v2/util"
)
@ -150,7 +150,8 @@ func (q *QuotaCheckResult) GetRemainingFiles() int {
// S3FsConfig defines the configuration for S3 based filesystem
type S3FsConfig struct {
sdk.S3FsConfig
sdk.BaseS3FsConfig
AccessSecret *kms.Secret `json:"access_secret,omitempty"`
}
// HideConfidentialData hides confidential data
@ -287,7 +288,8 @@ func (c *S3FsConfig) Validate() error {
// GCSFsConfig defines the configuration for Google Cloud Storage based filesystem
type GCSFsConfig struct {
sdk.GCSFsConfig
sdk.BaseGCSFsConfig
Credentials *kms.Secret `json:"credentials,omitempty"`
}
// HideConfidentialData hides confidential data
@ -358,7 +360,12 @@ func (c *GCSFsConfig) Validate(credentialsFilePath string) error {
// AzBlobFsConfig defines the configuration for Azure Blob Storage based filesystem
type AzBlobFsConfig struct {
sdk.AzBlobFsConfig
sdk.BaseAzBlobFsConfig
// Storage Account Key leave blank to use SAS URL.
// The access key is stored encrypted based on the kms configuration
AccountKey *kms.Secret `json:"account_key,omitempty"`
// Shared access signature URL, leave blank if using account/key
SASURL *kms.Secret `json:"sas_url,omitempty"`
}
// HideConfidentialData hides confidential data
@ -489,7 +496,7 @@ func (c *AzBlobFsConfig) Validate() error {
// CryptFsConfig defines the configuration to store local files as encrypted
type CryptFsConfig struct {
sdk.CryptFsConfig
Passphrase *kms.Secret `json:"passphrase,omitempty"`
}
// HideConfidentialData hides confidential data

View file

@ -22,8 +22,8 @@ import (
"github.com/drakkan/sftpgo/v2/common"
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/util"
"github.com/drakkan/sftpgo/v2/vfs"
)

View file

@ -31,9 +31,10 @@ import (
"github.com/drakkan/sftpgo/v2/dataprovider"
"github.com/drakkan/sftpgo/v2/httpclient"
"github.com/drakkan/sftpgo/v2/httpdtest"
"github.com/drakkan/sftpgo/v2/kms"
"github.com/drakkan/sftpgo/v2/logger"
"github.com/drakkan/sftpgo/v2/sdk"
"github.com/drakkan/sftpgo/v2/sdk/kms"
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
"github.com/drakkan/sftpgo/v2/sftpd"
"github.com/drakkan/sftpgo/v2/vfs"
"github.com/drakkan/sftpgo/v2/webdavd"
@ -1621,7 +1622,7 @@ func TestLoginWithDatabaseCredentials(t *testing.T) {
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus())
assert.Equal(t, sdkkms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus())
assert.NotEmpty(t, user.FsConfig.GCSConfig.Credentials.GetPayload())
assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetAdditionalData())
assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetKey())
@ -2443,11 +2444,11 @@ func TestSFTPLoopVirtualFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.SFTPFilesystemProvider,
SFTPConfig: vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user2.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
},
},
},
@ -2455,11 +2456,11 @@ func TestSFTPLoopVirtualFolders(t *testing.T) {
})
user2.FsConfig.Provider = sdk.SFTPFilesystemProvider
user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
SFTPFsConfig: sdk.SFTPFsConfig{
BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
Endpoint: sftpServerAddr,
Username: user1.Username,
Password: kms.NewPlainSecret(defaultPassword),
},
Password: kms.NewPlainSecret(defaultPassword),
}
user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated)
@ -2506,11 +2507,9 @@ func TestNestedVirtualFolders(t *testing.T) {
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
CryptFsConfig: sdk.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
},
MappedPath: mappedPathCrypt,
},
VirtualPath: vdirCryptPath,