mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 09:00:27 +00:00
azblob: store SAS URL as kms.Secret
This commit is contained in:
parent
8607788975
commit
9d3d7db29c
26 changed files with 1026 additions and 72 deletions
|
@ -176,7 +176,7 @@ Please take a look at the usage below to customize the serving parameters`,
|
||||||
AccountKey: kms.NewPlainSecret(portableAzAccountKey),
|
AccountKey: kms.NewPlainSecret(portableAzAccountKey),
|
||||||
Endpoint: portableAzEndpoint,
|
Endpoint: portableAzEndpoint,
|
||||||
AccessTier: portableAzAccessTier,
|
AccessTier: portableAzAccessTier,
|
||||||
SASURL: portableAzSASURL,
|
SASURL: kms.NewPlainSecret(portableAzSASURL),
|
||||||
KeyPrefix: portableAzKeyPrefix,
|
KeyPrefix: portableAzKeyPrefix,
|
||||||
UseEmulator: portableAzUseEmulator,
|
UseEmulator: portableAzUseEmulator,
|
||||||
UploadPartSize: int64(portableAzULPartSize),
|
UploadPartSize: int64(portableAzULPartSize),
|
||||||
|
|
|
@ -117,9 +117,7 @@ func newActionNotification(
|
||||||
bucket = fsConfig.GCSConfig.Bucket
|
bucket = fsConfig.GCSConfig.Bucket
|
||||||
case vfs.AzureBlobFilesystemProvider:
|
case vfs.AzureBlobFilesystemProvider:
|
||||||
bucket = fsConfig.AzBlobConfig.Container
|
bucket = fsConfig.AzBlobConfig.Container
|
||||||
if fsConfig.AzBlobConfig.SASURL != "" {
|
if fsConfig.AzBlobConfig.Endpoint != "" {
|
||||||
endpoint = fsConfig.AzBlobConfig.SASURL
|
|
||||||
} else {
|
|
||||||
endpoint = fsConfig.AzBlobConfig.Endpoint
|
endpoint = fsConfig.AzBlobConfig.Endpoint
|
||||||
}
|
}
|
||||||
case vfs.SFTPFilesystemProvider:
|
case vfs.SFTPFilesystemProvider:
|
||||||
|
|
|
@ -29,7 +29,6 @@ func TestNewActionNotification(t *testing.T) {
|
||||||
}
|
}
|
||||||
user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
|
user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
|
||||||
Container: "azcontainer",
|
Container: "azcontainer",
|
||||||
SASURL: "azsasurl",
|
|
||||||
Endpoint: "azendpoint",
|
Endpoint: "azendpoint",
|
||||||
}
|
}
|
||||||
user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
|
user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
|
||||||
|
@ -56,10 +55,9 @@ func TestNewActionNotification(t *testing.T) {
|
||||||
user.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
user.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
||||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, 0, nil)
|
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, 0, nil)
|
||||||
assert.Equal(t, "azcontainer", a.Bucket)
|
assert.Equal(t, "azcontainer", a.Bucket)
|
||||||
assert.Equal(t, "azsasurl", a.Endpoint)
|
assert.Equal(t, "azendpoint", a.Endpoint)
|
||||||
assert.Equal(t, 1, a.Status)
|
assert.Equal(t, 1, a.Status)
|
||||||
|
|
||||||
user.FsConfig.AzBlobConfig.SASURL = ""
|
|
||||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, os.O_APPEND, nil)
|
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, os.O_APPEND, nil)
|
||||||
assert.Equal(t, "azcontainer", a.Bucket)
|
assert.Equal(t, "azcontainer", a.Bucket)
|
||||||
assert.Equal(t, "azendpoint", a.Endpoint)
|
assert.Equal(t, "azendpoint", a.Endpoint)
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
boltDatabaseVersion = 6
|
boltDatabaseVersion = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -229,7 +229,7 @@ func (p *BoltProvider) adminExists(username string) (Admin, error) {
|
||||||
var admin Admin
|
var admin Admin
|
||||||
|
|
||||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getAdminBucket(tx)
|
bucket, err := getAdminsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ func (p *BoltProvider) addAdmin(admin *Admin) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getAdminBucket(tx)
|
bucket, err := getAdminsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ func (p *BoltProvider) updateAdmin(admin *Admin) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getAdminBucket(tx)
|
bucket, err := getAdminsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ func (p *BoltProvider) updateAdmin(admin *Admin) error {
|
||||||
|
|
||||||
func (p *BoltProvider) deleteAdmin(admin *Admin) error {
|
func (p *BoltProvider) deleteAdmin(admin *Admin) error {
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getAdminBucket(tx)
|
bucket, err := getAdminsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ func (p *BoltProvider) getAdmins(limit int, offset int, order string) ([]Admin,
|
||||||
admins := make([]Admin, 0, limit)
|
admins := make([]Admin, 0, limit)
|
||||||
|
|
||||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getAdminBucket(tx)
|
bucket, err := getAdminsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ func (p *BoltProvider) getAdmins(limit int, offset int, order string) ([]Admin,
|
||||||
func (p *BoltProvider) dumpAdmins() ([]Admin, error) {
|
func (p *BoltProvider) dumpAdmins() ([]Admin, error) {
|
||||||
admins := make([]Admin, 0, 30)
|
admins := make([]Admin, 0, 30)
|
||||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getAdminBucket(tx)
|
bucket, err := getAdminsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,7 @@ func (p *BoltProvider) userExists(username string) (User, error) {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", username)}
|
return &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", username)}
|
||||||
}
|
}
|
||||||
folderBucket, err := getFolderBucket(tx)
|
folderBucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,7 @@ func (p *BoltProvider) addUser(user *User) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
folderBucket, err := getFolderBucket(tx)
|
folderBucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -459,7 +459,7 @@ func (p *BoltProvider) updateUser(user *User) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
folderBucket, err := getFolderBucket(tx)
|
folderBucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -504,7 +504,7 @@ func (p *BoltProvider) deleteUser(user *User) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(user.VirtualFolders) > 0 {
|
if len(user.VirtualFolders) > 0 {
|
||||||
folderBucket, err := getFolderBucket(tx)
|
folderBucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -530,7 +530,7 @@ func (p *BoltProvider) dumpUsers() ([]User, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
folderBucket, err := getFolderBucket(tx)
|
folderBucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -562,7 +562,7 @@ func (p *BoltProvider) getUsers(limit int, offset int, order string) ([]User, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
folderBucket, err := getFolderBucket(tx)
|
folderBucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -607,7 +607,7 @@ func (p *BoltProvider) getUsers(limit int, offset int, order string) ([]User, er
|
||||||
func (p *BoltProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
|
func (p *BoltProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
|
||||||
folders := make([]vfs.BaseVirtualFolder, 0, 50)
|
folders := make([]vfs.BaseVirtualFolder, 0, 50)
|
||||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -632,7 +632,7 @@ func (p *BoltProvider) getFolders(limit, offset int, order string) ([]vfs.BaseVi
|
||||||
return folders, err
|
return folders, err
|
||||||
}
|
}
|
||||||
err = p.dbHandle.View(func(tx *bolt.Tx) error {
|
err = p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -681,7 +681,7 @@ func (p *BoltProvider) getFolders(limit, offset int, order string) ([]vfs.BaseVi
|
||||||
func (p *BoltProvider) getFolderByName(name string) (vfs.BaseVirtualFolder, error) {
|
func (p *BoltProvider) getFolderByName(name string) (vfs.BaseVirtualFolder, error) {
|
||||||
var folder vfs.BaseVirtualFolder
|
var folder vfs.BaseVirtualFolder
|
||||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -697,7 +697,7 @@ func (p *BoltProvider) addFolder(folder *vfs.BaseVirtualFolder) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -715,7 +715,7 @@ func (p *BoltProvider) updateFolder(folder *vfs.BaseVirtualFolder) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -745,7 +745,7 @@ func (p *BoltProvider) updateFolder(folder *vfs.BaseVirtualFolder) error {
|
||||||
|
|
||||||
func (p *BoltProvider) deleteFolder(folder *vfs.BaseVirtualFolder) error {
|
func (p *BoltProvider) deleteFolder(folder *vfs.BaseVirtualFolder) error {
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -795,7 +795,7 @@ func (p *BoltProvider) deleteFolder(folder *vfs.BaseVirtualFolder) error {
|
||||||
|
|
||||||
func (p *BoltProvider) updateFolderQuota(name string, filesAdd int, sizeAdd int64, reset bool) error {
|
func (p *BoltProvider) updateFolderQuota(name string, filesAdd int, sizeAdd int64, reset bool) error {
|
||||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket, err := getFolderBucket(tx)
|
bucket, err := getFoldersBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -860,6 +860,8 @@ func (p *BoltProvider) migrateDatabase() error {
|
||||||
providerLog(logger.LevelError, "%v", err)
|
providerLog(logger.LevelError, "%v", err)
|
||||||
logger.ErrorToConsole("%v", err)
|
logger.ErrorToConsole("%v", err)
|
||||||
return err
|
return err
|
||||||
|
case version == 6:
|
||||||
|
return updateBoltDatabaseFrom6To10(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
if version > boltDatabaseVersion {
|
if version > boltDatabaseVersion {
|
||||||
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
||||||
|
@ -883,6 +885,9 @@ func (p *BoltProvider) revertDatabase(targetVersion int) error {
|
||||||
if dbVersion.Version == targetVersion {
|
if dbVersion.Version == targetVersion {
|
||||||
return errors.New("current version match target version, nothing to do")
|
return errors.New("current version match target version, nothing to do")
|
||||||
}
|
}
|
||||||
|
if dbVersion.Version == 10 {
|
||||||
|
return downgradeBoltDatabaseFrom10To6(p.dbHandle)
|
||||||
|
}
|
||||||
return errors.New("the current version cannot be reverted")
|
return errors.New("the current version cannot be reverted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -991,7 +996,7 @@ func removeUserFromFolderMapping(folder *vfs.VirtualFolder, user *User, bucket *
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAdminBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
func getAdminsBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
bucket := tx.Bucket(adminsBucket)
|
bucket := tx.Bucket(adminsBucket)
|
||||||
|
@ -1010,7 +1015,7 @@ func getUsersBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
||||||
return bucket, err
|
return bucket, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFolderBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
func getFoldersBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
||||||
var err error
|
var err error
|
||||||
bucket := tx.Bucket(foldersBucket)
|
bucket := tx.Bucket(foldersBucket)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
|
@ -1038,7 +1043,7 @@ func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
|
||||||
return dbVersion, err
|
return dbVersion, err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
|
func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
|
||||||
err := dbHandle.Update(func(tx *bolt.Tx) error {
|
err := dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
bucket := tx.Bucket(dbVersionBucket)
|
bucket := tx.Bucket(dbVersionBucket)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
|
@ -1054,4 +1059,317 @@ func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
|
||||||
return bucket.Put(dbVersionKey, buf)
|
return bucket.Put(dbVersionKey, buf)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
func updateBoltDatabaseFrom6To10(dbHandle *bolt.DB) error {
|
||||||
|
logger.InfoToConsole("updating database version: 6 -> 10")
|
||||||
|
providerLog(logger.LevelInfo, "updating database version: 6 -> 10")
|
||||||
|
|
||||||
|
if err := boltUpdateV7Folders(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := boltUpdateV7Users(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateBoltDatabaseVersion(dbHandle, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func downgradeBoltDatabaseFrom10To6(dbHandle *bolt.DB) error {
|
||||||
|
logger.InfoToConsole("downgrading database version: 10 -> 6")
|
||||||
|
providerLog(logger.LevelInfo, "downgrading database version: 10 -> 6")
|
||||||
|
|
||||||
|
if err := boltDowngradeV7Folders(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := boltDowngradeV7Users(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateBoltDatabaseVersion(dbHandle, 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
func boltUpdateV7Folders(dbHandle *bolt.DB) error {
|
||||||
|
var folders []map[string]interface{}
|
||||||
|
err := dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getFoldersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||||
|
var folderMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(v, &folderMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fsBytes, err := json.Marshal(folderMap["filesystem"])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var compatFsConfig compatFilesystemV9
|
||||||
|
err = json.Unmarshal(fsBytes, &compatFsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v9 fsconfig for folder %#v, is it already migrated?", folderMap["name"])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if compatFsConfig.AzBlobConfig.SASURL != "" {
|
||||||
|
folder := vfs.BaseVirtualFolder{
|
||||||
|
Name: folderMap["name"].(string),
|
||||||
|
}
|
||||||
|
fsConfig, err := convertFsConfigFromV9(compatFsConfig, folder.GetEncrytionAdditionalData())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
folderMap["filesystem"] = fsConfig
|
||||||
|
folders = append(folders, folderMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getFoldersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, folder := range folders {
|
||||||
|
buf, err := json.Marshal(folder)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = bucket.Put([]byte(folder["name"].(string)), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func boltUpdateV7Users(dbHandle *bolt.DB) error {
|
||||||
|
var users []map[string]interface{}
|
||||||
|
err := dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getUsersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||||
|
var userMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(v, &userMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fsBytes, err := json.Marshal(userMap["filesystem"])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foldersBytes, err := json.Marshal(userMap["virtual_folders"])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var compatFsConfig compatFilesystemV9
|
||||||
|
err = json.Unmarshal(fsBytes, &compatFsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v9 fsconfig for user %#v, is it already migrated?", userMap["name"])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var compatFolders []compatFolderV9
|
||||||
|
err = json.Unmarshal(foldersBytes, &compatFolders)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v9 folders for user %#v, is it already migrated?", userMap["name"])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toConvert := false
|
||||||
|
for idx := range compatFolders {
|
||||||
|
f := &compatFolders[idx]
|
||||||
|
if f.FsConfig.AzBlobConfig.SASURL != "" {
|
||||||
|
f.FsConfig.AzBlobConfig = compatAzBlobFsConfigV9{}
|
||||||
|
toConvert = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if compatFsConfig.AzBlobConfig.SASURL != "" {
|
||||||
|
user := User{
|
||||||
|
Username: userMap["username"].(string),
|
||||||
|
}
|
||||||
|
fsConfig, err := convertFsConfigFromV9(compatFsConfig, user.GetEncrytionAdditionalData())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userMap["filesystem"] = fsConfig
|
||||||
|
toConvert = true
|
||||||
|
}
|
||||||
|
if toConvert {
|
||||||
|
userMap["virtual_folders"] = compatFolders
|
||||||
|
users = append(users, userMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getUsersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, user := range users {
|
||||||
|
buf, err := json.Marshal(user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = bucket.Put([]byte(user["username"].(string)), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl
|
||||||
|
func boltDowngradeV7Folders(dbHandle *bolt.DB) error {
|
||||||
|
var folders []map[string]interface{}
|
||||||
|
err := dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getFoldersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||||
|
var folderMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(v, &folderMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fsBytes, err := json.Marshal(folderMap["filesystem"])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var fsConfig vfs.Filesystem
|
||||||
|
err = json.Unmarshal(fsBytes, &fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v10 fsconfig for folder %#v, is it already migrated?", folderMap["name"])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fsConfig.AzBlobConfig.SASURL != nil && !fsConfig.AzBlobConfig.SASURL.IsEmpty() {
|
||||||
|
fsV9, err := convertFsConfigToV9(fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
folderMap["filesystem"] = fsV9
|
||||||
|
folders = append(folders, folderMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getFoldersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, folder := range folders {
|
||||||
|
buf, err := json.Marshal(folder)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = bucket.Put([]byte(folder["name"].(string)), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl,gocyclo
|
||||||
|
func boltDowngradeV7Users(dbHandle *bolt.DB) error {
|
||||||
|
var users []map[string]interface{}
|
||||||
|
err := dbHandle.View(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getUsersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cursor := bucket.Cursor()
|
||||||
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||||
|
var userMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(v, &userMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fsBytes, err := json.Marshal(userMap["filesystem"])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foldersBytes, err := json.Marshal(userMap["virtual_folders"])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var fsConfig vfs.Filesystem
|
||||||
|
err = json.Unmarshal(fsBytes, &fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v10 fsconfig for user %#v, is it already migrated?", userMap["username"])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var folders []vfs.VirtualFolder
|
||||||
|
err = json.Unmarshal(foldersBytes, &folders)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v9 folders for user %#v, is it already migrated?", userMap["name"])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toConvert := false
|
||||||
|
for idx := range folders {
|
||||||
|
f := &folders[idx]
|
||||||
|
f.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{}
|
||||||
|
toConvert = true
|
||||||
|
}
|
||||||
|
if fsConfig.AzBlobConfig.SASURL != nil && !fsConfig.AzBlobConfig.SASURL.IsEmpty() {
|
||||||
|
fsV9, err := convertFsConfigToV9(fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userMap["filesystem"] = fsV9
|
||||||
|
toConvert = true
|
||||||
|
}
|
||||||
|
if toConvert {
|
||||||
|
userMap["virtual_folders"] = folders
|
||||||
|
users = append(users, userMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbHandle.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket, err := getUsersBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, user := range users {
|
||||||
|
buf, err := json.Marshal(user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = bucket.Put([]byte(user["username"].(string)), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
118
dataprovider/compat.go
Normal file
118
dataprovider/compat.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package dataprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/drakkan/sftpgo/kms"
|
||||||
|
"github.com/drakkan/sftpgo/vfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type compatAzBlobFsConfigV9 struct {
|
||||||
|
Container string `json:"container,omitempty"`
|
||||||
|
AccountName string `json:"account_name,omitempty"`
|
||||||
|
AccountKey *kms.Secret `json:"account_key,omitempty"`
|
||||||
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
|
SASURL string `json:"sas_url,omitempty"`
|
||||||
|
KeyPrefix string `json:"key_prefix,omitempty"`
|
||||||
|
UploadPartSize int64 `json:"upload_part_size,omitempty"`
|
||||||
|
UploadConcurrency int `json:"upload_concurrency,omitempty"`
|
||||||
|
UseEmulator bool `json:"use_emulator,omitempty"`
|
||||||
|
AccessTier string `json:"access_tier,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type compatFilesystemV9 struct {
|
||||||
|
Provider vfs.FilesystemProvider `json:"provider"`
|
||||||
|
S3Config vfs.S3FsConfig `json:"s3config,omitempty"`
|
||||||
|
GCSConfig vfs.GCSFsConfig `json:"gcsconfig,omitempty"`
|
||||||
|
AzBlobConfig compatAzBlobFsConfigV9 `json:"azblobconfig,omitempty"`
|
||||||
|
CryptConfig vfs.CryptFsConfig `json:"cryptconfig,omitempty"`
|
||||||
|
SFTPConfig vfs.SFTPFsConfig `json:"sftpconfig,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type compatBaseFolderV9 struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
MappedPath string `json:"mapped_path,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
UsedQuotaSize int64 `json:"used_quota_size"`
|
||||||
|
UsedQuotaFiles int `json:"used_quota_files"`
|
||||||
|
LastQuotaUpdate int64 `json:"last_quota_update"`
|
||||||
|
Users []string `json:"users,omitempty"`
|
||||||
|
FsConfig compatFilesystemV9 `json:"filesystem"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type compatFolderV9 struct {
|
||||||
|
compatBaseFolderV9
|
||||||
|
VirtualPath string `json:"virtual_path"`
|
||||||
|
QuotaSize int64 `json:"quota_size"`
|
||||||
|
QuotaFiles int `json:"quota_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type compatUserV9 struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
FsConfig compatFilesystemV9 `json:"filesystem"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFsConfigFromV9(compatFs compatFilesystemV9, aead string) (vfs.Filesystem, error) {
|
||||||
|
fsConfig := vfs.Filesystem{
|
||||||
|
Provider: compatFs.Provider,
|
||||||
|
S3Config: compatFs.S3Config,
|
||||||
|
GCSConfig: compatFs.GCSConfig,
|
||||||
|
CryptConfig: compatFs.CryptConfig,
|
||||||
|
SFTPConfig: compatFs.SFTPConfig,
|
||||||
|
}
|
||||||
|
azSASURL := kms.NewEmptySecret()
|
||||||
|
if compatFs.Provider == vfs.AzureBlobFilesystemProvider && compatFs.AzBlobConfig.SASURL != "" {
|
||||||
|
azSASURL = kms.NewPlainSecret(compatFs.AzBlobConfig.SASURL)
|
||||||
|
}
|
||||||
|
if compatFs.AzBlobConfig.AccountKey == nil {
|
||||||
|
compatFs.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
||||||
|
}
|
||||||
|
fsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
|
||||||
|
Container: compatFs.AzBlobConfig.Container,
|
||||||
|
AccountName: compatFs.AzBlobConfig.AccountName,
|
||||||
|
AccountKey: compatFs.AzBlobConfig.AccountKey,
|
||||||
|
Endpoint: compatFs.AzBlobConfig.Endpoint,
|
||||||
|
SASURL: azSASURL,
|
||||||
|
KeyPrefix: compatFs.AzBlobConfig.KeyPrefix,
|
||||||
|
UploadPartSize: compatFs.AzBlobConfig.UploadPartSize,
|
||||||
|
UploadConcurrency: compatFs.AzBlobConfig.UploadConcurrency,
|
||||||
|
UseEmulator: compatFs.AzBlobConfig.UseEmulator,
|
||||||
|
AccessTier: compatFs.AzBlobConfig.AccessTier,
|
||||||
|
}
|
||||||
|
err := fsConfig.AzBlobConfig.EncryptCredentials(aead)
|
||||||
|
return fsConfig, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFsConfigToV9(fs vfs.Filesystem) (compatFilesystemV9, error) {
|
||||||
|
azSASURL := ""
|
||||||
|
if fs.Provider == vfs.AzureBlobFilesystemProvider {
|
||||||
|
if fs.AzBlobConfig.SASURL != nil && fs.AzBlobConfig.SASURL.IsEncrypted() {
|
||||||
|
err := fs.AzBlobConfig.SASURL.Decrypt()
|
||||||
|
if err != nil {
|
||||||
|
return compatFilesystemV9{}, err
|
||||||
|
}
|
||||||
|
azSASURL = fs.AzBlobConfig.SASURL.GetPayload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
azFsCompat := compatAzBlobFsConfigV9{
|
||||||
|
Container: fs.AzBlobConfig.Container,
|
||||||
|
AccountName: fs.AzBlobConfig.AccountName,
|
||||||
|
AccountKey: fs.AzBlobConfig.AccountKey,
|
||||||
|
Endpoint: fs.AzBlobConfig.Endpoint,
|
||||||
|
SASURL: azSASURL,
|
||||||
|
KeyPrefix: fs.AzBlobConfig.KeyPrefix,
|
||||||
|
UploadPartSize: fs.AzBlobConfig.UploadPartSize,
|
||||||
|
UploadConcurrency: fs.AzBlobConfig.UploadConcurrency,
|
||||||
|
UseEmulator: fs.AzBlobConfig.UseEmulator,
|
||||||
|
AccessTier: fs.AzBlobConfig.AccessTier,
|
||||||
|
}
|
||||||
|
fsV9 := compatFilesystemV9{
|
||||||
|
Provider: fs.Provider,
|
||||||
|
S3Config: fs.S3Config,
|
||||||
|
GCSConfig: fs.GCSConfig,
|
||||||
|
AzBlobConfig: azFsCompat,
|
||||||
|
CryptConfig: fs.CryptConfig,
|
||||||
|
SFTPConfig: fs.SFTPConfig,
|
||||||
|
}
|
||||||
|
return fsV9, nil
|
||||||
|
}
|
|
@ -66,7 +66,7 @@ const (
|
||||||
CockroachDataProviderName = "cockroachdb"
|
CockroachDataProviderName = "cockroachdb"
|
||||||
// DumpVersion defines the version for the dump.
|
// DumpVersion defines the version for the dump.
|
||||||
// For restore/load we support the current version and the previous one
|
// For restore/load we support the current version and the previous one
|
||||||
DumpVersion = 7
|
DumpVersion = 8
|
||||||
|
|
||||||
argonPwdPrefix = "$argon2id$"
|
argonPwdPrefix = "$argon2id$"
|
||||||
bcryptPwdPrefix = "$2a$"
|
bcryptPwdPrefix = "$2a$"
|
||||||
|
|
|
@ -250,6 +250,8 @@ func (p *MySQLProvider) migrateDatabase() error {
|
||||||
return err
|
return err
|
||||||
case version == 8:
|
case version == 8:
|
||||||
return updateMySQLDatabaseFromV8(p.dbHandle)
|
return updateMySQLDatabaseFromV8(p.dbHandle)
|
||||||
|
case version == 9:
|
||||||
|
return updateMySQLDatabaseFromV9(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
if version > sqlDatabaseVersion {
|
if version > sqlDatabaseVersion {
|
||||||
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
||||||
|
@ -274,19 +276,35 @@ func (p *MySQLProvider) revertDatabase(targetVersion int) error {
|
||||||
switch dbVersion.Version {
|
switch dbVersion.Version {
|
||||||
case 9:
|
case 9:
|
||||||
return downgradeMySQLDatabaseFromV9(p.dbHandle)
|
return downgradeMySQLDatabaseFromV9(p.dbHandle)
|
||||||
|
case 10:
|
||||||
|
return downgradeMySQLDatabaseFromV10(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMySQLDatabaseFromV8(dbHandle *sql.DB) error {
|
func updateMySQLDatabaseFromV8(dbHandle *sql.DB) error {
|
||||||
return updateMySQLDatabaseFrom8To9(dbHandle)
|
if err := updateMySQLDatabaseFrom8To9(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateMySQLDatabaseFromV9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateMySQLDatabaseFromV9(dbHandle *sql.DB) error {
|
||||||
|
return updateMySQLDatabaseFrom9To10(dbHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func downgradeMySQLDatabaseFromV9(dbHandle *sql.DB) error {
|
func downgradeMySQLDatabaseFromV9(dbHandle *sql.DB) error {
|
||||||
return downgradeMySQLDatabaseFrom9To8(dbHandle)
|
return downgradeMySQLDatabaseFrom9To8(dbHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downgradeMySQLDatabaseFromV10(dbHandle *sql.DB) error {
|
||||||
|
if err := downgradeMySQLDatabaseFrom10To9(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return downgradeMySQLDatabaseFromV9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
func updateMySQLDatabaseFrom8To9(dbHandle *sql.DB) error {
|
func updateMySQLDatabaseFrom8To9(dbHandle *sql.DB) error {
|
||||||
logger.InfoToConsole("updating database version: 8 -> 9")
|
logger.InfoToConsole("updating database version: 8 -> 9")
|
||||||
providerLog(logger.LevelInfo, "updating database version: 8 -> 9")
|
providerLog(logger.LevelInfo, "updating database version: 8 -> 9")
|
||||||
|
@ -304,3 +322,11 @@ func downgradeMySQLDatabaseFrom9To8(dbHandle *sql.DB) error {
|
||||||
sql = strings.ReplaceAll(sql, "{{folders}}", sqlTableFolders)
|
sql = strings.ReplaceAll(sql, "{{folders}}", sqlTableFolders)
|
||||||
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 8)
|
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateMySQLDatabaseFrom9To10(dbHandle *sql.DB) error {
|
||||||
|
return sqlCommonUpdateDatabaseFrom9To10(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func downgradeMySQLDatabaseFrom10To9(dbHandle *sql.DB) error {
|
||||||
|
return sqlCommonDowngradeDatabaseFrom10To9(dbHandle)
|
||||||
|
}
|
||||||
|
|
|
@ -263,6 +263,8 @@ func (p *PGSQLProvider) migrateDatabase() error {
|
||||||
return err
|
return err
|
||||||
case version == 8:
|
case version == 8:
|
||||||
return updatePGSQLDatabaseFromV8(p.dbHandle)
|
return updatePGSQLDatabaseFromV8(p.dbHandle)
|
||||||
|
case version == 9:
|
||||||
|
return updatePGSQLDatabaseFromV9(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
if version > sqlDatabaseVersion {
|
if version > sqlDatabaseVersion {
|
||||||
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
||||||
|
@ -287,19 +289,35 @@ func (p *PGSQLProvider) revertDatabase(targetVersion int) error {
|
||||||
switch dbVersion.Version {
|
switch dbVersion.Version {
|
||||||
case 9:
|
case 9:
|
||||||
return downgradePGSQLDatabaseFromV9(p.dbHandle)
|
return downgradePGSQLDatabaseFromV9(p.dbHandle)
|
||||||
|
case 10:
|
||||||
|
return downgradePGSQLDatabaseFromV10(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePGSQLDatabaseFromV8(dbHandle *sql.DB) error {
|
func updatePGSQLDatabaseFromV8(dbHandle *sql.DB) error {
|
||||||
return updatePGSQLDatabaseFrom8To9(dbHandle)
|
if err := updatePGSQLDatabaseFrom8To9(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updatePGSQLDatabaseFromV9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePGSQLDatabaseFromV9(dbHandle *sql.DB) error {
|
||||||
|
return updatePGSQLDatabaseFrom9To10(dbHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func downgradePGSQLDatabaseFromV9(dbHandle *sql.DB) error {
|
func downgradePGSQLDatabaseFromV9(dbHandle *sql.DB) error {
|
||||||
return downgradePGSQLDatabaseFrom9To8(dbHandle)
|
return downgradePGSQLDatabaseFrom9To8(dbHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downgradePGSQLDatabaseFromV10(dbHandle *sql.DB) error {
|
||||||
|
if err := downgradePGSQLDatabaseFrom10To9(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return downgradePGSQLDatabaseFromV9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
func updatePGSQLDatabaseFrom8To9(dbHandle *sql.DB) error {
|
func updatePGSQLDatabaseFrom8To9(dbHandle *sql.DB) error {
|
||||||
logger.InfoToConsole("updating database version: 8 -> 9")
|
logger.InfoToConsole("updating database version: 8 -> 9")
|
||||||
providerLog(logger.LevelInfo, "updating database version: 8 -> 9")
|
providerLog(logger.LevelInfo, "updating database version: 8 -> 9")
|
||||||
|
@ -317,3 +335,11 @@ func downgradePGSQLDatabaseFrom9To8(dbHandle *sql.DB) error {
|
||||||
sql = strings.ReplaceAll(sql, "{{folders}}", sqlTableFolders)
|
sql = strings.ReplaceAll(sql, "{{folders}}", sqlTableFolders)
|
||||||
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 8)
|
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updatePGSQLDatabaseFrom9To10(dbHandle *sql.DB) error {
|
||||||
|
return sqlCommonUpdateDatabaseFrom9To10(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func downgradePGSQLDatabaseFrom10To9(dbHandle *sql.DB) error {
|
||||||
|
return sqlCommonDowngradeDatabaseFrom10To9(dbHandle)
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sqlDatabaseVersion = 9
|
sqlDatabaseVersion = 10
|
||||||
defaultSQLQueryTimeout = 10 * time.Second
|
defaultSQLQueryTimeout = 10 * time.Second
|
||||||
longSQLQueryTimeout = 60 * time.Second
|
longSQLQueryTimeout = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
@ -1096,3 +1096,313 @@ func sqlCommonExecuteTx(ctx context.Context, dbHandle *sql.DB, txFn func(*sql.Tx
|
||||||
}
|
}
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sqlCommonUpdateDatabaseFrom9To10(dbHandle *sql.DB) error {
|
||||||
|
logger.InfoToConsole("updating database version: 9 -> 10")
|
||||||
|
providerLog(logger.LevelInfo, "updating database version: 9 -> 10")
|
||||||
|
|
||||||
|
if err := sqlCommonUpdateV10Folders(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sqlCommonUpdateV10Users(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
return sqlCommonUpdateDatabaseVersion(ctx, dbHandle, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sqlCommonDowngradeDatabaseFrom10To9(dbHandle *sql.DB) error {
|
||||||
|
logger.InfoToConsole("downgrading database version: 10 -> 9")
|
||||||
|
providerLog(logger.LevelInfo, "downgrading database version: 10 -> 9")
|
||||||
|
|
||||||
|
if err := sqlCommonDowngradeV10Folders(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sqlCommonDowngradeV10Users(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
return sqlCommonUpdateDatabaseVersion(ctx, dbHandle, 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl
|
||||||
|
func sqlCommonDowngradeV10Folders(dbHandle *sql.DB) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
q := getCompatFolderV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.QueryContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var folders []compatBaseFolderV9
|
||||||
|
for rows.Next() {
|
||||||
|
var folder compatBaseFolderV9
|
||||||
|
var fsConfigString sql.NullString
|
||||||
|
err = rows.Scan(&folder.ID, &folder.Name, &fsConfigString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fsConfigString.Valid {
|
||||||
|
var fsConfig vfs.Filesystem
|
||||||
|
err = json.Unmarshal([]byte(fsConfigString.String), &fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v10 fsconfig for folder %#v, is it already migrated?", folder.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fsConfig.AzBlobConfig.SASURL != nil && !fsConfig.AzBlobConfig.SASURL.IsEmpty() {
|
||||||
|
fsV9, err := convertFsConfigToV9(fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
folder.FsConfig = fsV9
|
||||||
|
folders = append(folders, folder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// update fsconfig for affected folders
|
||||||
|
for _, folder := range folders {
|
||||||
|
q := updateCompatFolderV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
cfg, err := json.Marshal(folder.FsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.ExecContext(ctx, string(cfg), folder.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl
|
||||||
|
func sqlCommonDowngradeV10Users(dbHandle *sql.DB) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
q := getCompatUserV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.QueryContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var users []compatUserV9
|
||||||
|
for rows.Next() {
|
||||||
|
var user compatUserV9
|
||||||
|
var fsConfigString sql.NullString
|
||||||
|
err = rows.Scan(&user.ID, &user.Username, &fsConfigString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fsConfigString.Valid {
|
||||||
|
var fsConfig vfs.Filesystem
|
||||||
|
err = json.Unmarshal([]byte(fsConfigString.String), &fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v10 fsconfig for user %#v, is it already migrated?", user.Username)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fsConfig.AzBlobConfig.SASURL != nil && !fsConfig.AzBlobConfig.SASURL.IsEmpty() {
|
||||||
|
fsV9, err := convertFsConfigToV9(fsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.FsConfig = fsV9
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// update fsconfig for affected users
|
||||||
|
for _, user := range users {
|
||||||
|
q := updateCompatUserV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
cfg, err := json.Marshal(user.FsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.ExecContext(ctx, string(cfg), user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sqlCommonUpdateV10Folders(dbHandle *sql.DB) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
q := getCompatFolderV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.QueryContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var folders []vfs.BaseVirtualFolder
|
||||||
|
for rows.Next() {
|
||||||
|
var folder vfs.BaseVirtualFolder
|
||||||
|
var fsConfigString sql.NullString
|
||||||
|
err = rows.Scan(&folder.ID, &folder.Name, &fsConfigString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fsConfigString.Valid {
|
||||||
|
var compatFsConfig compatFilesystemV9
|
||||||
|
err = json.Unmarshal([]byte(fsConfigString.String), &compatFsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v9 fsconfig for folder %#v, is it already migrated?", folder.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if compatFsConfig.AzBlobConfig.SASURL != "" {
|
||||||
|
fsConfig, err := convertFsConfigFromV9(compatFsConfig, folder.GetEncrytionAdditionalData())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
folder.FsConfig = fsConfig
|
||||||
|
folders = append(folders, folder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// update fsconfig for affected folders
|
||||||
|
for _, folder := range folders {
|
||||||
|
q := updateCompatFolderV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
cfg, err := json.Marshal(folder.FsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.ExecContext(ctx, string(cfg), folder.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sqlCommonUpdateV10Users(dbHandle *sql.DB) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
q := getCompatUserV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.QueryContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
for rows.Next() {
|
||||||
|
var user User
|
||||||
|
var fsConfigString sql.NullString
|
||||||
|
err = rows.Scan(&user.ID, &user.Username, &fsConfigString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fsConfigString.Valid {
|
||||||
|
var compatFsConfig compatFilesystemV9
|
||||||
|
err = json.Unmarshal([]byte(fsConfigString.String), &compatFsConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("failed to unmarshal v9 fsconfig for user %#v, is it already migrated?", user.Username)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if compatFsConfig.AzBlobConfig.SASURL != "" {
|
||||||
|
fsConfig, err := convertFsConfigFromV9(compatFsConfig, user.GetEncrytionAdditionalData())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.FsConfig = fsConfig
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// update fsconfig for affected users
|
||||||
|
for _, user := range users {
|
||||||
|
q := updateCompatUserV10FsConfigQuery()
|
||||||
|
stmt, err := dbHandle.PrepareContext(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
providerLog(logger.LevelWarn, "error preparing database query %#v: %v", q, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
cfg, err := json.Marshal(user.FsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.ExecContext(ctx, string(cfg), user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -271,6 +271,8 @@ func (p *SQLiteProvider) migrateDatabase() error {
|
||||||
return err
|
return err
|
||||||
case version == 8:
|
case version == 8:
|
||||||
return updateSQLiteDatabaseFromV8(p.dbHandle)
|
return updateSQLiteDatabaseFromV8(p.dbHandle)
|
||||||
|
case version == 9:
|
||||||
|
return updateSQLiteDatabaseFromV9(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
if version > sqlDatabaseVersion {
|
if version > sqlDatabaseVersion {
|
||||||
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
providerLog(logger.LevelWarn, "database version %v is newer than the supported one: %v", version,
|
||||||
|
@ -295,19 +297,35 @@ func (p *SQLiteProvider) revertDatabase(targetVersion int) error {
|
||||||
switch dbVersion.Version {
|
switch dbVersion.Version {
|
||||||
case 9:
|
case 9:
|
||||||
return downgradeSQLiteDatabaseFromV9(p.dbHandle)
|
return downgradeSQLiteDatabaseFromV9(p.dbHandle)
|
||||||
|
case 10:
|
||||||
|
return downgradeSQLiteDatabaseFromV10(p.dbHandle)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("database version not handled: %v", dbVersion.Version)
|
return fmt.Errorf("database version not handled: %v", dbVersion.Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSQLiteDatabaseFromV8(dbHandle *sql.DB) error {
|
func updateSQLiteDatabaseFromV8(dbHandle *sql.DB) error {
|
||||||
return updateSQLiteDatabaseFrom8To9(dbHandle)
|
if err := updateSQLiteDatabaseFrom8To9(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateSQLiteDatabaseFromV9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSQLiteDatabaseFromV9(dbHandle *sql.DB) error {
|
||||||
|
return updateSQLiteDatabaseFrom9To10(dbHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func downgradeSQLiteDatabaseFromV9(dbHandle *sql.DB) error {
|
func downgradeSQLiteDatabaseFromV9(dbHandle *sql.DB) error {
|
||||||
return downgradeSQLiteDatabaseFrom9To8(dbHandle)
|
return downgradeSQLiteDatabaseFrom9To8(dbHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downgradeSQLiteDatabaseFromV10(dbHandle *sql.DB) error {
|
||||||
|
if err := downgradeSQLiteDatabaseFrom10To9(dbHandle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return downgradeSQLiteDatabaseFromV9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
func updateSQLiteDatabaseFrom8To9(dbHandle *sql.DB) error {
|
func updateSQLiteDatabaseFrom8To9(dbHandle *sql.DB) error {
|
||||||
logger.InfoToConsole("updating database version: 8 -> 9")
|
logger.InfoToConsole("updating database version: 8 -> 9")
|
||||||
providerLog(logger.LevelInfo, "updating database version: 8 -> 9")
|
providerLog(logger.LevelInfo, "updating database version: 8 -> 9")
|
||||||
|
@ -332,6 +350,14 @@ func downgradeSQLiteDatabaseFrom9To8(dbHandle *sql.DB) error {
|
||||||
return setPragmaFK(dbHandle, "ON")
|
return setPragmaFK(dbHandle, "ON")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateSQLiteDatabaseFrom9To10(dbHandle *sql.DB) error {
|
||||||
|
return sqlCommonUpdateDatabaseFrom9To10(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func downgradeSQLiteDatabaseFrom10To9(dbHandle *sql.DB) error {
|
||||||
|
return sqlCommonDowngradeDatabaseFrom10To9(dbHandle)
|
||||||
|
}
|
||||||
|
|
||||||
func setPragmaFK(dbHandle *sql.DB, value string) error {
|
func setPragmaFK(dbHandle *sql.DB, value string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
|
@ -210,3 +210,19 @@ func getDatabaseVersionQuery() string {
|
||||||
func getUpdateDBVersionQuery() string {
|
func getUpdateDBVersionQuery() string {
|
||||||
return fmt.Sprintf(`UPDATE %v SET version=%v`, sqlTableSchemaVersion, sqlPlaceholders[0])
|
return fmt.Sprintf(`UPDATE %v SET version=%v`, sqlTableSchemaVersion, sqlPlaceholders[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCompatUserV10FsConfigQuery() string {
|
||||||
|
return fmt.Sprintf(`SELECT id,username,filesystem FROM %v`, sqlTableUsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCompatUserV10FsConfigQuery() string {
|
||||||
|
return fmt.Sprintf(`UPDATE %v SET filesystem=%v WHERE id=%v`, sqlTableUsers, sqlPlaceholders[0], sqlPlaceholders[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCompatFolderV10FsConfigQuery() string {
|
||||||
|
return fmt.Sprintf(`SELECT id,name,filesystem FROM %v`, sqlTableFolders)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCompatFolderV10FsConfigQuery() string {
|
||||||
|
return fmt.Sprintf(`UPDATE %v SET filesystem=%v WHERE id=%v`, sqlTableFolders, sqlPlaceholders[0], sqlPlaceholders[1])
|
||||||
|
}
|
||||||
|
|
|
@ -349,6 +349,7 @@ func (u *User) hideConfidentialData() {
|
||||||
u.FsConfig.GCSConfig.Credentials.Hide()
|
u.FsConfig.GCSConfig.Credentials.Hide()
|
||||||
case vfs.AzureBlobFilesystemProvider:
|
case vfs.AzureBlobFilesystemProvider:
|
||||||
u.FsConfig.AzBlobConfig.AccountKey.Hide()
|
u.FsConfig.AzBlobConfig.AccountKey.Hide()
|
||||||
|
u.FsConfig.AzBlobConfig.SASURL.Hide()
|
||||||
case vfs.CryptedFilesystemProvider:
|
case vfs.CryptedFilesystemProvider:
|
||||||
u.FsConfig.CryptConfig.Passphrase.Hide()
|
u.FsConfig.CryptConfig.Passphrase.Hide()
|
||||||
case vfs.SFTPFilesystemProvider:
|
case vfs.SFTPFilesystemProvider:
|
||||||
|
@ -399,6 +400,9 @@ func (u *User) hasRedactedSecret() bool {
|
||||||
if u.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
|
if u.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if u.FsConfig.AzBlobConfig.SASURL.IsRedacted() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
case vfs.CryptedFilesystemProvider:
|
case vfs.CryptedFilesystemProvider:
|
||||||
if u.FsConfig.CryptConfig.Passphrase.IsRedacted() {
|
if u.FsConfig.CryptConfig.Passphrase.IsRedacted() {
|
||||||
return true
|
return true
|
||||||
|
@ -457,6 +461,7 @@ func (u *User) SetEmptySecrets() {
|
||||||
u.FsConfig.S3Config.AccessSecret = kms.NewEmptySecret()
|
u.FsConfig.S3Config.AccessSecret = kms.NewEmptySecret()
|
||||||
u.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
|
u.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
|
||||||
u.FsConfig.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
u.FsConfig.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
||||||
|
u.FsConfig.AzBlobConfig.SASURL = kms.NewEmptySecret()
|
||||||
u.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
|
u.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
|
||||||
u.FsConfig.SFTPConfig.Password = kms.NewEmptySecret()
|
u.FsConfig.SFTPConfig.Password = kms.NewEmptySecret()
|
||||||
u.FsConfig.SFTPConfig.PrivateKey = kms.NewEmptySecret()
|
u.FsConfig.SFTPConfig.PrivateKey = kms.NewEmptySecret()
|
||||||
|
|
|
@ -40,7 +40,7 @@ The external program can also read the following environment variables:
|
||||||
- `SFTPGO_ACTION_FILE_SIZE`, non-zero for `pre-upload`,`upload`, `download` and `delete` actions if the file size is greater than `0`
|
- `SFTPGO_ACTION_FILE_SIZE`, non-zero for `pre-upload`,`upload`, `download` and `delete` actions if the file size is greater than `0`
|
||||||
- `SFTPGO_ACTION_FS_PROVIDER`, `0` for local filesystem, `1` for S3 backend, `2` for Google Cloud Storage (GCS) backend, `3` for Azure Blob Storage backend, `4` for local encrypted backend, `5` for SFTP backend
|
- `SFTPGO_ACTION_FS_PROVIDER`, `0` for local filesystem, `1` for S3 backend, `2` for Google Cloud Storage (GCS) backend, `3` for Azure Blob Storage backend, `4` for local encrypted backend, `5` for SFTP backend
|
||||||
- `SFTPGO_ACTION_BUCKET`, non-empty for S3, GCS and Azure backends
|
- `SFTPGO_ACTION_BUCKET`, non-empty for S3, GCS and Azure backends
|
||||||
- `SFTPGO_ACTION_ENDPOINT`, non-empty for S3, SFTP and Azure backend if configured. For Azure this is the SAS URL, if configured otherwise the endpoint
|
- `SFTPGO_ACTION_ENDPOINT`, non-empty for S3, SFTP and Azure backend if configured. For Azure this is the endpoint, if configured
|
||||||
- `SFTPGO_ACTION_STATUS`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error
|
- `SFTPGO_ACTION_STATUS`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error
|
||||||
- `SFTPGO_ACTION_PROTOCOL`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP`
|
- `SFTPGO_ACTION_PROTOCOL`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP`
|
||||||
- `SFTPGO_ACTION_OPEN_FLAGS`, integer. File open flags, can be non-zero for `pre-upload` action. If `SFTPGO_ACTION_FILE_SIZE` is greater than zero and `SFTPGO_ACTION_OPEN_FLAGS&512 == 0` the target file will not be truncated
|
- `SFTPGO_ACTION_OPEN_FLAGS`, integer. File open flags, can be non-zero for `pre-upload` action. If `SFTPGO_ACTION_FILE_SIZE` is greater than zero and `SFTPGO_ACTION_OPEN_FLAGS&512 == 0` the target file will not be truncated
|
||||||
|
@ -58,7 +58,7 @@ If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. Th
|
||||||
- `file_size`, included for `pre-upload`, `upload`, `download`, `delete` actions if the file size is greater than `0`
|
- `file_size`, included for `pre-upload`, `upload`, `download`, `delete` actions if the file size is greater than `0`
|
||||||
- `fs_provider`, `0` for local filesystem, `1` for S3 backend, `2` for Google Cloud Storage (GCS) backend, `3` for Azure Blob Storage backend, `4` for local encrypted backend, `5` for SFTP backend
|
- `fs_provider`, `0` for local filesystem, `1` for S3 backend, `2` for Google Cloud Storage (GCS) backend, `3` for Azure Blob Storage backend, `4` for local encrypted backend, `5` for SFTP backend
|
||||||
- `bucket`, inlcuded for S3, GCS and Azure backends
|
- `bucket`, inlcuded for S3, GCS and Azure backends
|
||||||
- `endpoint`, included for S3, SFTP and Azure backend if configured. For Azure this is the SAS URL, if configured, otherwise the endpoint
|
- `endpoint`, included for S3, SFTP and Azure backend if configured. For Azure this is the endpoint, if configured
|
||||||
- `status`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error
|
- `status`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error
|
||||||
- `protocol`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP`
|
- `protocol`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP`
|
||||||
- `open_flags`, integer. File open flags, can be non-zero for `pre-upload` action. If `file_size` is greater than zero and `file_size&512 == 0` the target file will not be truncated
|
- `open_flags`, integer. File open flags, can be non-zero for `pre-upload` action. If `file_size` is greater than zero and `file_size&512 == 0` the target file will not be truncated
|
||||||
|
|
|
@ -54,6 +54,7 @@ func updateFolder(w http.ResponseWriter, r *http.Request) {
|
||||||
folderID := folder.ID
|
folderID := folder.ID
|
||||||
currentS3AccessSecret := folder.FsConfig.S3Config.AccessSecret
|
currentS3AccessSecret := folder.FsConfig.S3Config.AccessSecret
|
||||||
currentAzAccountKey := folder.FsConfig.AzBlobConfig.AccountKey
|
currentAzAccountKey := folder.FsConfig.AzBlobConfig.AccountKey
|
||||||
|
currentAzSASUrl := folder.FsConfig.AzBlobConfig.SASURL
|
||||||
currentGCSCredentials := folder.FsConfig.GCSConfig.Credentials
|
currentGCSCredentials := folder.FsConfig.GCSConfig.Credentials
|
||||||
currentCryptoPassphrase := folder.FsConfig.CryptConfig.Passphrase
|
currentCryptoPassphrase := folder.FsConfig.CryptConfig.Passphrase
|
||||||
currentSFTPPassword := folder.FsConfig.SFTPConfig.Password
|
currentSFTPPassword := folder.FsConfig.SFTPConfig.Password
|
||||||
|
@ -72,7 +73,7 @@ func updateFolder(w http.ResponseWriter, r *http.Request) {
|
||||||
folder.ID = folderID
|
folder.ID = folderID
|
||||||
folder.Name = name
|
folder.Name = name
|
||||||
folder.FsConfig.SetEmptySecretsIfNil()
|
folder.FsConfig.SetEmptySecretsIfNil()
|
||||||
updateEncryptedSecrets(&folder.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentGCSCredentials,
|
updateEncryptedSecrets(&folder.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl, currentGCSCredentials,
|
||||||
currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey)
|
currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey)
|
||||||
err = dataprovider.UpdateFolder(&folder, users)
|
err = dataprovider.UpdateFolder(&folder, users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -74,6 +74,10 @@ func addUser(w http.ResponseWriter, r *http.Request) {
|
||||||
sendAPIResponse(w, r, errors.New("invalid account_key"), "", http.StatusBadRequest)
|
sendAPIResponse(w, r, errors.New("invalid account_key"), "", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if user.FsConfig.AzBlobConfig.SASURL.IsRedacted() {
|
||||||
|
sendAPIResponse(w, r, errors.New("invalid sas_url"), "", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
case vfs.CryptedFilesystemProvider:
|
case vfs.CryptedFilesystemProvider:
|
||||||
if user.FsConfig.CryptConfig.Passphrase.IsRedacted() {
|
if user.FsConfig.CryptConfig.Passphrase.IsRedacted() {
|
||||||
sendAPIResponse(w, r, errors.New("invalid passphrase"), "", http.StatusBadRequest)
|
sendAPIResponse(w, r, errors.New("invalid passphrase"), "", http.StatusBadRequest)
|
||||||
|
@ -120,6 +124,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
||||||
currentPermissions := user.Permissions
|
currentPermissions := user.Permissions
|
||||||
currentS3AccessSecret := user.FsConfig.S3Config.AccessSecret
|
currentS3AccessSecret := user.FsConfig.S3Config.AccessSecret
|
||||||
currentAzAccountKey := user.FsConfig.AzBlobConfig.AccountKey
|
currentAzAccountKey := user.FsConfig.AzBlobConfig.AccountKey
|
||||||
|
currentAzSASUrl := user.FsConfig.AzBlobConfig.SASURL
|
||||||
currentGCSCredentials := user.FsConfig.GCSConfig.Credentials
|
currentGCSCredentials := user.FsConfig.GCSConfig.Credentials
|
||||||
currentCryptoPassphrase := user.FsConfig.CryptConfig.Passphrase
|
currentCryptoPassphrase := user.FsConfig.CryptConfig.Passphrase
|
||||||
currentSFTPPassword := user.FsConfig.SFTPConfig.Password
|
currentSFTPPassword := user.FsConfig.SFTPConfig.Password
|
||||||
|
@ -144,8 +149,8 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
||||||
if len(user.Permissions) == 0 {
|
if len(user.Permissions) == 0 {
|
||||||
user.Permissions = currentPermissions
|
user.Permissions = currentPermissions
|
||||||
}
|
}
|
||||||
updateEncryptedSecrets(&user.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentGCSCredentials, currentCryptoPassphrase,
|
updateEncryptedSecrets(&user.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl,
|
||||||
currentSFTPPassword, currentSFTPKey)
|
currentGCSCredentials, currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey)
|
||||||
err = dataprovider.UpdateUser(&user)
|
err = dataprovider.UpdateUser(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||||
|
@ -176,7 +181,7 @@ func disconnectUser(username string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateEncryptedSecrets(fsConfig *vfs.Filesystem, currentS3AccessSecret, currentAzAccountKey,
|
func updateEncryptedSecrets(fsConfig *vfs.Filesystem, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl,
|
||||||
currentGCSCredentials, currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey *kms.Secret) {
|
currentGCSCredentials, currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey *kms.Secret) {
|
||||||
// we use the new access secret if plain or empty, otherwise the old value
|
// we use the new access secret if plain or empty, otherwise the old value
|
||||||
switch fsConfig.Provider {
|
switch fsConfig.Provider {
|
||||||
|
@ -188,6 +193,9 @@ func updateEncryptedSecrets(fsConfig *vfs.Filesystem, currentS3AccessSecret, cur
|
||||||
if fsConfig.AzBlobConfig.AccountKey.IsNotPlainAndNotEmpty() {
|
if fsConfig.AzBlobConfig.AccountKey.IsNotPlainAndNotEmpty() {
|
||||||
fsConfig.AzBlobConfig.AccountKey = currentAzAccountKey
|
fsConfig.AzBlobConfig.AccountKey = currentAzAccountKey
|
||||||
}
|
}
|
||||||
|
if fsConfig.AzBlobConfig.SASURL.IsNotPlainAndNotEmpty() {
|
||||||
|
fsConfig.AzBlobConfig.SASURL = currentAzSASUrl
|
||||||
|
}
|
||||||
case vfs.GCSFilesystemProvider:
|
case vfs.GCSFilesystemProvider:
|
||||||
if fsConfig.GCSConfig.Credentials.IsNotPlainAndNotEmpty() {
|
if fsConfig.GCSConfig.Credentials.IsNotPlainAndNotEmpty() {
|
||||||
fsConfig.GCSConfig.Credentials = currentGCSCredentials
|
fsConfig.GCSConfig.Credentials = currentGCSCredentials
|
||||||
|
|
|
@ -984,10 +984,13 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
|
||||||
|
|
||||||
u = getTestUser()
|
u = getTestUser()
|
||||||
u.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
u.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
||||||
u.FsConfig.AzBlobConfig.SASURL = "http://foo\x7f.com/"
|
u.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("http://foo\x7f.com/")
|
||||||
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
|
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
u.FsConfig.AzBlobConfig.SASURL = ""
|
u.FsConfig.AzBlobConfig.SASURL = kms.NewSecret(kms.SecretStatusRedacted, "key", "", "")
|
||||||
|
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
u.FsConfig.AzBlobConfig.SASURL = kms.NewEmptySecret()
|
||||||
u.FsConfig.AzBlobConfig.AccountName = "name"
|
u.FsConfig.AzBlobConfig.AccountName = "name"
|
||||||
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
|
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -1802,9 +1805,9 @@ func TestUserAzureBlobConfig(t *testing.T) {
|
||||||
assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
|
assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
|
||||||
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
|
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
|
||||||
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
|
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
|
||||||
// test user without access key and access secret (sas)
|
// test user without access key and access secret (SAS)
|
||||||
user.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
user.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
||||||
user.FsConfig.AzBlobConfig.SASURL = "https://myaccount.blob.core.windows.net/pictures/profile.jpg?sv=2012-02-12&st=2009-02-09&se=2009-02-10&sr=c&sp=r&si=YWJjZGVmZw%3d%3d&sig=dD80ihBh5jfNpymO5Hg1IdiJIEvHcJpCMiCMnN%2fRnbI%3d"
|
user.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("https://myaccount.blob.core.windows.net/pictures/profile.jpg?sv=2012-02-12&st=2009-02-09&se=2009-02-10&sr=c&sp=r&si=YWJjZGVmZw%3d%3d&sig=dD80ihBh5jfNpymO5Hg1IdiJIEvHcJpCMiCMnN%2fRnbI%3d")
|
||||||
user.FsConfig.AzBlobConfig.KeyPrefix = "somedir/subdir"
|
user.FsConfig.AzBlobConfig.KeyPrefix = "somedir/subdir"
|
||||||
user.FsConfig.AzBlobConfig.AccountName = ""
|
user.FsConfig.AzBlobConfig.AccountName = ""
|
||||||
user.FsConfig.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
user.FsConfig.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
||||||
|
@ -1813,14 +1816,34 @@ func TestUserAzureBlobConfig(t *testing.T) {
|
||||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
|
assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
|
||||||
|
assert.NotNil(t, user.FsConfig.AzBlobConfig.SASURL)
|
||||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
user.Password = defaultPassword
|
user.Password = defaultPassword
|
||||||
user.ID = 0
|
user.ID = 0
|
||||||
// sas test for add instead of update
|
// sas test for add instead of update
|
||||||
|
user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
|
||||||
|
Container: user.FsConfig.AzBlobConfig.Container,
|
||||||
|
SASURL: kms.NewPlainSecret("http://127.0.0.1/fake/sass/url"),
|
||||||
|
}
|
||||||
user, _, err = httpdtest.AddUser(user, http.StatusCreated)
|
user, _, err = httpdtest.AddUser(user, http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
|
assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
|
||||||
|
initialPayload = user.FsConfig.AzBlobConfig.SASURL.GetPayload()
|
||||||
|
assert.Equal(t, kms.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.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, initialPayload, user.FsConfig.AzBlobConfig.SASURL.GetPayload())
|
||||||
|
assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
|
||||||
|
assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetKey())
|
||||||
|
|
||||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
@ -7718,6 +7741,7 @@ func TestWebUserGCSMock(t *testing.T) {
|
||||||
err = os.Remove(credentialsFilePath)
|
err = os.Remove(credentialsFilePath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebUserAzureBlobMock(t *testing.T) {
|
func TestWebUserAzureBlobMock(t *testing.T) {
|
||||||
webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
|
webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -7763,7 +7787,6 @@ func TestWebUserAzureBlobMock(t *testing.T) {
|
||||||
form.Set("az_container", user.FsConfig.AzBlobConfig.Container)
|
form.Set("az_container", user.FsConfig.AzBlobConfig.Container)
|
||||||
form.Set("az_account_name", user.FsConfig.AzBlobConfig.AccountName)
|
form.Set("az_account_name", user.FsConfig.AzBlobConfig.AccountName)
|
||||||
form.Set("az_account_key", user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
|
form.Set("az_account_key", user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
|
||||||
form.Set("az_sas_url", user.FsConfig.AzBlobConfig.SASURL)
|
|
||||||
form.Set("az_endpoint", user.FsConfig.AzBlobConfig.Endpoint)
|
form.Set("az_endpoint", user.FsConfig.AzBlobConfig.Endpoint)
|
||||||
form.Set("az_key_prefix", user.FsConfig.AzBlobConfig.KeyPrefix)
|
form.Set("az_key_prefix", user.FsConfig.AzBlobConfig.KeyPrefix)
|
||||||
form.Set("az_use_emulator", "checked")
|
form.Set("az_use_emulator", "checked")
|
||||||
|
@ -7810,7 +7833,6 @@ func TestWebUserAzureBlobMock(t *testing.T) {
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.Container, user.FsConfig.AzBlobConfig.Container)
|
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.Container, user.FsConfig.AzBlobConfig.Container)
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.AccountName, user.FsConfig.AzBlobConfig.AccountName)
|
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.AccountName, user.FsConfig.AzBlobConfig.AccountName)
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.Endpoint, user.FsConfig.AzBlobConfig.Endpoint)
|
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.Endpoint, user.FsConfig.AzBlobConfig.Endpoint)
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.SASURL, user.FsConfig.AzBlobConfig.SASURL)
|
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.KeyPrefix, user.FsConfig.AzBlobConfig.KeyPrefix)
|
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.KeyPrefix, user.FsConfig.AzBlobConfig.KeyPrefix)
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadPartSize, user.FsConfig.AzBlobConfig.UploadPartSize)
|
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, updateUser.FsConfig.AzBlobConfig.UploadConcurrency, user.FsConfig.AzBlobConfig.UploadConcurrency)
|
||||||
|
@ -7838,6 +7860,49 @@ func TestWebUserAzureBlobMock(t *testing.T) {
|
||||||
assert.Equal(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetPayload(), lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetPayload())
|
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.GetKey())
|
||||||
assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
|
assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
|
||||||
|
// test SAS url
|
||||||
|
user.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("sasurl")
|
||||||
|
form.Set("az_account_name", "")
|
||||||
|
form.Set("az_account_key", "")
|
||||||
|
form.Set("az_container", "")
|
||||||
|
form.Set("az_sas_url", user.FsConfig.AzBlobConfig.SASURL.GetPayload())
|
||||||
|
b, contentType, _ = getMultipartFormData(form, "", "")
|
||||||
|
req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
|
||||||
|
setJWTCookieForReq(req, webToken)
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusSeeOther, rr)
|
||||||
|
req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
|
||||||
|
setBearerForReq(req, apiToken)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
updateUser = dataprovider.User{}
|
||||||
|
err = render.DecodeJSON(rr.Body, &updateUser)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, kms.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())
|
||||||
|
// now check that a redacted sas url is not saved
|
||||||
|
form.Set("az_sas_url", redactedSecret)
|
||||||
|
b, contentType, _ = getMultipartFormData(form, "", "")
|
||||||
|
req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
|
||||||
|
setJWTCookieForReq(req, webToken)
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusSeeOther, rr)
|
||||||
|
req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
|
||||||
|
setBearerForReq(req, apiToken)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
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, 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())
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
|
req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
|
||||||
setBearerForReq(req, apiToken)
|
setBearerForReq(req, apiToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
|
|
|
@ -2194,8 +2194,7 @@ components:
|
||||||
account_key:
|
account_key:
|
||||||
$ref: '#/components/schemas/Secret'
|
$ref: '#/components/schemas/Secret'
|
||||||
sas_url:
|
sas_url:
|
||||||
type: string
|
$ref: '#/components/schemas/Secret'
|
||||||
description: 'Shared access signature URL, leave blank if using account/key'
|
|
||||||
endpoint:
|
endpoint:
|
||||||
type: string
|
type: string
|
||||||
description: '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"'
|
description: '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"'
|
||||||
|
|
|
@ -739,7 +739,7 @@ func getAzureConfig(r *http.Request) (vfs.AzBlobFsConfig, error) {
|
||||||
config.Container = r.Form.Get("az_container")
|
config.Container = r.Form.Get("az_container")
|
||||||
config.AccountName = r.Form.Get("az_account_name")
|
config.AccountName = r.Form.Get("az_account_name")
|
||||||
config.AccountKey = getSecretFromFormField(r, "az_account_key")
|
config.AccountKey = getSecretFromFormField(r, "az_account_key")
|
||||||
config.SASURL = r.Form.Get("az_sas_url")
|
config.SASURL = getSecretFromFormField(r, "az_sas_url")
|
||||||
config.Endpoint = r.Form.Get("az_endpoint")
|
config.Endpoint = r.Form.Get("az_endpoint")
|
||||||
config.KeyPrefix = r.Form.Get("az_key_prefix")
|
config.KeyPrefix = r.Form.Get("az_key_prefix")
|
||||||
config.AccessTier = r.Form.Get("az_access_tier")
|
config.AccessTier = r.Form.Get("az_access_tier")
|
||||||
|
@ -1457,8 +1457,8 @@ func handleWebUpdateUserPost(w http.ResponseWriter, r *http.Request) {
|
||||||
updatedUser.Password = user.Password
|
updatedUser.Password = user.Password
|
||||||
}
|
}
|
||||||
updateEncryptedSecrets(&updatedUser.FsConfig, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey,
|
updateEncryptedSecrets(&updatedUser.FsConfig, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey,
|
||||||
user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase, user.FsConfig.SFTPConfig.Password,
|
user.FsConfig.AzBlobConfig.SASURL, user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase,
|
||||||
user.FsConfig.SFTPConfig.PrivateKey)
|
user.FsConfig.SFTPConfig.Password, user.FsConfig.SFTPConfig.PrivateKey)
|
||||||
|
|
||||||
err = dataprovider.UpdateUser(&updatedUser)
|
err = dataprovider.UpdateUser(&updatedUser)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -1569,8 +1569,8 @@ func handleWebUpdateFolderPost(w http.ResponseWriter, r *http.Request) {
|
||||||
updatedFolder.FsConfig = fsConfig
|
updatedFolder.FsConfig = fsConfig
|
||||||
updatedFolder.FsConfig.SetEmptySecretsIfNil()
|
updatedFolder.FsConfig.SetEmptySecretsIfNil()
|
||||||
updateEncryptedSecrets(&updatedFolder.FsConfig, folder.FsConfig.S3Config.AccessSecret, folder.FsConfig.AzBlobConfig.AccountKey,
|
updateEncryptedSecrets(&updatedFolder.FsConfig, folder.FsConfig.S3Config.AccessSecret, folder.FsConfig.AzBlobConfig.AccountKey,
|
||||||
folder.FsConfig.GCSConfig.Credentials, folder.FsConfig.CryptConfig.Passphrase, folder.FsConfig.SFTPConfig.Password,
|
folder.FsConfig.AzBlobConfig.SASURL, folder.FsConfig.GCSConfig.Credentials, folder.FsConfig.CryptConfig.Passphrase,
|
||||||
folder.FsConfig.SFTPConfig.PrivateKey)
|
folder.FsConfig.SFTPConfig.Password, folder.FsConfig.SFTPConfig.PrivateKey)
|
||||||
|
|
||||||
err = dataprovider.UpdateFolder(updatedFolder, folder.Users)
|
err = dataprovider.UpdateFolder(updatedFolder, folder.Users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1132,8 +1132,8 @@ func compareAzBlobConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error
|
||||||
if expected.AzBlobConfig.Endpoint != actual.AzBlobConfig.Endpoint {
|
if expected.AzBlobConfig.Endpoint != actual.AzBlobConfig.Endpoint {
|
||||||
return errors.New("azure Blob endpoint mismatch")
|
return errors.New("azure Blob endpoint mismatch")
|
||||||
}
|
}
|
||||||
if expected.AzBlobConfig.SASURL != actual.AzBlobConfig.SASURL {
|
if err := checkEncryptedSecret(expected.AzBlobConfig.SASURL, actual.AzBlobConfig.SASURL); err != nil {
|
||||||
return errors.New("azure Blob SASL URL mismatch")
|
return fmt.Errorf("azure Blob SAS URL mismatch: %v", err)
|
||||||
}
|
}
|
||||||
if expected.AzBlobConfig.UploadPartSize != actual.AzBlobConfig.UploadPartSize {
|
if expected.AzBlobConfig.UploadPartSize != actual.AzBlobConfig.UploadPartSize {
|
||||||
return errors.New("azure Blob upload part size mismatch")
|
return errors.New("azure Blob upload part size mismatch")
|
||||||
|
|
|
@ -282,6 +282,11 @@ func (s *Service) configurePortableSecrets() {
|
||||||
if payload != "" {
|
if payload != "" {
|
||||||
s.PortableUser.FsConfig.AzBlobConfig.AccountKey = kms.NewPlainSecret(payload)
|
s.PortableUser.FsConfig.AzBlobConfig.AccountKey = kms.NewPlainSecret(payload)
|
||||||
}
|
}
|
||||||
|
payload = s.PortableUser.FsConfig.AzBlobConfig.SASURL.GetPayload()
|
||||||
|
s.PortableUser.FsConfig.AzBlobConfig.SASURL = kms.NewEmptySecret()
|
||||||
|
if payload != "" {
|
||||||
|
s.PortableUser.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret(payload)
|
||||||
|
}
|
||||||
case vfs.CryptedFilesystemProvider:
|
case vfs.CryptedFilesystemProvider:
|
||||||
payload := s.PortableUser.FsConfig.CryptConfig.Passphrase.GetPayload()
|
payload := s.PortableUser.FsConfig.CryptConfig.Passphrase.GetPayload()
|
||||||
s.PortableUser.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
|
s.PortableUser.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
|
||||||
|
|
|
@ -7243,7 +7243,7 @@ func TestStatVFSCloudBackend(t *testing.T) {
|
||||||
usePubKey := true
|
usePubKey := true
|
||||||
u := getTestUser(usePubKey)
|
u := getTestUser(usePubKey)
|
||||||
u.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
u.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
|
||||||
u.FsConfig.AzBlobConfig.SASURL = "https://myaccount.blob.core.windows.net/sasurl"
|
u.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("https://myaccount.blob.core.windows.net/sasurl")
|
||||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
conn, client, err := getSftpClient(user, usePubKey)
|
conn, client, err := getSftpClient(user, usePubKey)
|
||||||
|
|
|
@ -161,8 +161,8 @@
|
||||||
<div class="form-group row azblob">
|
<div class="form-group row azblob">
|
||||||
<label for="idAzSASURL" class="col-sm-2 col-form-label">SAS URL</label>
|
<label for="idAzSASURL" class="col-sm-2 col-form-label">SAS URL</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" id="idAzSASURL" name="az_sas_url" placeholder=""
|
<input type="password" class="form-control" id="idAzSASURL" name="az_sas_url" placeholder=""
|
||||||
value="{{.AzBlobConfig.SASURL}}" maxlength="255">
|
value="{{if .AzBlobConfig.SASURL.IsEncrypted}}{{.RedactedSecret}}{{else}}{{.AzBlobConfig.SASURL.GetPayload}}{{end}}" maxlength="1000">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row azblob">
|
<div class="form-group row azblob">
|
||||||
|
|
|
@ -75,13 +75,16 @@ func NewAzBlobFs(connectionID, localTempDir, mountPath string, config AzBlobFsCo
|
||||||
if err := fs.config.AccountKey.TryDecrypt(); err != nil {
|
if err := fs.config.AccountKey.TryDecrypt(); err != nil {
|
||||||
return fs, err
|
return fs, err
|
||||||
}
|
}
|
||||||
|
if err := fs.config.SASURL.TryDecrypt(); err != nil {
|
||||||
|
return fs, err
|
||||||
|
}
|
||||||
fs.setConfigDefaults()
|
fs.setConfigDefaults()
|
||||||
|
|
||||||
version := version.Get()
|
version := version.Get()
|
||||||
telemetryValue := fmt.Sprintf("SFTPGo-%v_%v", version.Version, version.CommitHash)
|
telemetryValue := fmt.Sprintf("SFTPGo-%v_%v", version.Version, version.CommitHash)
|
||||||
|
|
||||||
if fs.config.SASURL != "" {
|
if fs.config.SASURL.GetPayload() != "" {
|
||||||
u, err := url.Parse(fs.config.SASURL)
|
u, err := url.Parse(fs.config.SASURL.GetPayload())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fs, fmt.Errorf("invalid credentials: %v", err)
|
return fs, fmt.Errorf("invalid credentials: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -144,8 +147,8 @@ func NewAzBlobFs(connectionID, localTempDir, mountPath string, config AzBlobFsCo
|
||||||
|
|
||||||
// Name returns the name for the Fs implementation
|
// Name returns the name for the Fs implementation
|
||||||
func (fs *AzureBlobFs) Name() string {
|
func (fs *AzureBlobFs) Name() string {
|
||||||
if fs.config.SASURL != "" {
|
if !fs.config.SASURL.IsEmpty() {
|
||||||
return fmt.Sprintf("Azure Blob SAS URL %#v", fs.config.Container)
|
return fmt.Sprintf("Azure Blob with SAS URL, container %#v", fs.config.Container)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Azure Blob container %#v", fs.config.Container)
|
return fmt.Sprintf("Azure Blob container %#v", fs.config.Container)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,9 @@ func (f *Filesystem) SetEmptySecretsIfNil() {
|
||||||
if f.AzBlobConfig.AccountKey == nil {
|
if f.AzBlobConfig.AccountKey == nil {
|
||||||
f.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
f.AzBlobConfig.AccountKey = kms.NewEmptySecret()
|
||||||
}
|
}
|
||||||
|
if f.AzBlobConfig.SASURL == nil {
|
||||||
|
f.AzBlobConfig.SASURL = kms.NewEmptySecret()
|
||||||
|
}
|
||||||
if f.CryptConfig.Passphrase == nil {
|
if f.CryptConfig.Passphrase == nil {
|
||||||
f.CryptConfig.Passphrase = kms.NewEmptySecret()
|
f.CryptConfig.Passphrase = kms.NewEmptySecret()
|
||||||
}
|
}
|
||||||
|
@ -61,6 +64,9 @@ func (f *Filesystem) SetNilSecretsIfEmpty() {
|
||||||
if f.AzBlobConfig.AccountKey != nil && f.AzBlobConfig.AccountKey.IsEmpty() {
|
if f.AzBlobConfig.AccountKey != nil && f.AzBlobConfig.AccountKey.IsEmpty() {
|
||||||
f.AzBlobConfig.AccountKey = nil
|
f.AzBlobConfig.AccountKey = nil
|
||||||
}
|
}
|
||||||
|
if f.AzBlobConfig.SASURL != nil && f.AzBlobConfig.SASURL.IsEmpty() {
|
||||||
|
f.AzBlobConfig.SASURL = nil
|
||||||
|
}
|
||||||
if f.CryptConfig.Passphrase != nil && f.CryptConfig.Passphrase.IsEmpty() {
|
if f.CryptConfig.Passphrase != nil && f.CryptConfig.Passphrase.IsEmpty() {
|
||||||
f.CryptConfig.Passphrase = nil
|
f.CryptConfig.Passphrase = nil
|
||||||
}
|
}
|
||||||
|
@ -122,7 +128,7 @@ func (f *Filesystem) GetACopy() Filesystem {
|
||||||
AccountName: f.AzBlobConfig.AccountName,
|
AccountName: f.AzBlobConfig.AccountName,
|
||||||
AccountKey: f.AzBlobConfig.AccountKey.Clone(),
|
AccountKey: f.AzBlobConfig.AccountKey.Clone(),
|
||||||
Endpoint: f.AzBlobConfig.Endpoint,
|
Endpoint: f.AzBlobConfig.Endpoint,
|
||||||
SASURL: f.AzBlobConfig.SASURL,
|
SASURL: f.AzBlobConfig.SASURL.Clone(),
|
||||||
KeyPrefix: f.AzBlobConfig.KeyPrefix,
|
KeyPrefix: f.AzBlobConfig.KeyPrefix,
|
||||||
UploadPartSize: f.AzBlobConfig.UploadPartSize,
|
UploadPartSize: f.AzBlobConfig.UploadPartSize,
|
||||||
UploadConcurrency: f.AzBlobConfig.UploadConcurrency,
|
UploadConcurrency: f.AzBlobConfig.UploadConcurrency,
|
||||||
|
|
|
@ -108,6 +108,7 @@ func (v *BaseVirtualFolder) hideConfidentialData() {
|
||||||
v.FsConfig.GCSConfig.Credentials.Hide()
|
v.FsConfig.GCSConfig.Credentials.Hide()
|
||||||
case AzureBlobFilesystemProvider:
|
case AzureBlobFilesystemProvider:
|
||||||
v.FsConfig.AzBlobConfig.AccountKey.Hide()
|
v.FsConfig.AzBlobConfig.AccountKey.Hide()
|
||||||
|
v.FsConfig.AzBlobConfig.SASURL.Hide()
|
||||||
case CryptedFilesystemProvider:
|
case CryptedFilesystemProvider:
|
||||||
v.FsConfig.CryptConfig.Passphrase.Hide()
|
v.FsConfig.CryptConfig.Passphrase.Hide()
|
||||||
case SFTPFilesystemProvider:
|
case SFTPFilesystemProvider:
|
||||||
|
@ -139,6 +140,9 @@ func (v *BaseVirtualFolder) HasRedactedSecret() bool {
|
||||||
if v.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
|
if v.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if v.FsConfig.AzBlobConfig.SASURL.IsRedacted() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
case CryptedFilesystemProvider:
|
case CryptedFilesystemProvider:
|
||||||
if v.FsConfig.CryptConfig.Passphrase.IsRedacted() {
|
if v.FsConfig.CryptConfig.Passphrase.IsRedacted() {
|
||||||
return true
|
return true
|
||||||
|
|
36
vfs/vfs.go
36
vfs/vfs.go
|
@ -342,9 +342,9 @@ type AzBlobFsConfig struct {
|
||||||
// for example "http://127.0.0.1:10000"
|
// for example "http://127.0.0.1:10000"
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
// Shared access signature URL, leave blank if using account/key
|
// Shared access signature URL, leave blank if using account/key
|
||||||
SASURL string `json:"sas_url,omitempty"`
|
SASURL *kms.Secret `json:"sas_url,omitempty"`
|
||||||
// KeyPrefix is similar to a chroot directory for local filesystem.
|
// KeyPrefix is similar to a chroot directory for local filesystem.
|
||||||
// If specified then the SFTPGo userd will only see objects that starts
|
// If specified then the SFTPGo user will only see objects that starts
|
||||||
// with this prefix and so you can restrict access to a specific
|
// with this prefix and so you can restrict access to a specific
|
||||||
// folder. The prefix, if not empty, must not start with "/" and must
|
// folder. The prefix, if not empty, must not start with "/" and must
|
||||||
// end with "/".
|
// end with "/".
|
||||||
|
@ -376,7 +376,13 @@ func (c *AzBlobFsConfig) isEqual(other *AzBlobFsConfig) bool {
|
||||||
if c.Endpoint != other.Endpoint {
|
if c.Endpoint != other.Endpoint {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c.SASURL != other.SASURL {
|
if c.SASURL.IsEmpty() {
|
||||||
|
c.SASURL = kms.NewEmptySecret()
|
||||||
|
}
|
||||||
|
if other.SASURL.IsEmpty() {
|
||||||
|
other.SASURL = kms.NewEmptySecret()
|
||||||
|
}
|
||||||
|
if !c.SASURL.IsEqual(other.SASURL) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c.KeyPrefix != other.KeyPrefix {
|
if c.KeyPrefix != other.KeyPrefix {
|
||||||
|
@ -411,10 +417,26 @@ func (c *AzBlobFsConfig) EncryptCredentials(additionalData string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if c.SASURL.IsPlain() {
|
||||||
|
c.SASURL.SetAdditionalData(additionalData)
|
||||||
|
if err := c.SASURL.Encrypt(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AzBlobFsConfig) checkCredentials() error {
|
func (c *AzBlobFsConfig) checkCredentials() error {
|
||||||
|
if c.SASURL.IsPlain() {
|
||||||
|
_, err := url.Parse(c.SASURL.GetPayload())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.SASURL.IsEncrypted() && !c.SASURL.IsValid() {
|
||||||
|
return errors.New("invalid encrypted sas_url")
|
||||||
|
}
|
||||||
|
if !c.SASURL.IsEmpty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if c.AccountName == "" || !c.AccountKey.IsValidInput() {
|
if c.AccountName == "" || !c.AccountKey.IsValidInput() {
|
||||||
return errors.New("credentials cannot be empty or invalid")
|
return errors.New("credentials cannot be empty or invalid")
|
||||||
}
|
}
|
||||||
|
@ -429,11 +451,11 @@ func (c *AzBlobFsConfig) Validate() error {
|
||||||
if c.AccountKey == nil {
|
if c.AccountKey == nil {
|
||||||
c.AccountKey = kms.NewEmptySecret()
|
c.AccountKey = kms.NewEmptySecret()
|
||||||
}
|
}
|
||||||
if c.SASURL != "" {
|
if c.SASURL == nil {
|
||||||
_, err := url.Parse(c.SASURL)
|
c.SASURL = kms.NewEmptySecret()
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
if c.Container == "" {
|
// container could be embedded within SAS URL we check this at runtime
|
||||||
|
if c.SASURL.IsEmpty() && c.Container == "" {
|
||||||
return errors.New("container cannot be empty")
|
return errors.New("container cannot be empty")
|
||||||
}
|
}
|
||||||
if err := c.checkCredentials(); err != nil {
|
if err := c.checkCredentials(); err != nil {
|
||||||
|
|
Loading…
Reference in a new issue