add Data At Rest Encryption support

This commit is contained in:
Nicola Murino
2020-12-05 13:48:13 +01:00
parent 95c6d41c35
commit 4a88ea5c03
38 changed files with 1754 additions and 139 deletions

View File

@@ -100,6 +100,11 @@ func addUser(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, errors.New("invalid account_key"), "", http.StatusBadRequest)
return
}
case dataprovider.CryptedFilesystemProvider:
if user.FsConfig.CryptConfig.Passphrase.IsRedacted() {
sendAPIResponse(w, r, errors.New("invalid passphrase"), "", http.StatusBadRequest)
return
}
}
err = dataprovider.AddUser(user)
if err == nil {
@@ -141,6 +146,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
var currentS3AccessSecret *kms.Secret
var currentAzAccountKey *kms.Secret
var currentGCSCredentials *kms.Secret
var currentCryptoPassphrase *kms.Secret
if user.FsConfig.Provider == dataprovider.S3FilesystemProvider {
currentS3AccessSecret = user.FsConfig.S3Config.AccessSecret
}
@@ -150,10 +156,15 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
if user.FsConfig.Provider == dataprovider.GCSFilesystemProvider {
currentGCSCredentials = user.FsConfig.GCSConfig.Credentials
}
if user.FsConfig.Provider == dataprovider.CryptedFilesystemProvider {
currentCryptoPassphrase = user.FsConfig.CryptConfig.Passphrase
}
user.Permissions = make(map[string][]string)
user.FsConfig.S3Config = vfs.S3FsConfig{}
user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{}
user.FsConfig.GCSConfig = vfs.GCSFsConfig{}
user.FsConfig.CryptConfig = vfs.CryptFsConfig{}
err = render.DecodeJSON(r.Body, &user)
if err != nil {
sendAPIResponse(w, r, err, "", http.StatusBadRequest)
@@ -164,8 +175,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
if len(user.Permissions) == 0 {
user.Permissions = currentPermissions
}
updateEncryptedSecrets(&user, currentS3AccessSecret, currentAzAccountKey, currentGCSCredentials)
updateEncryptedSecrets(&user, currentS3AccessSecret, currentAzAccountKey, currentGCSCredentials, currentCryptoPassphrase)
if user.ID != userID {
sendAPIResponse(w, r, err, "user ID in request body does not match user ID in path parameter", http.StatusBadRequest)
return
@@ -210,7 +220,8 @@ func disconnectUser(username string) {
}
}
func updateEncryptedSecrets(user *dataprovider.User, currentS3AccessSecret, currentAzAccountKey, currentGCSCredentials *kms.Secret) {
func updateEncryptedSecrets(user *dataprovider.User, currentS3AccessSecret, currentAzAccountKey,
currentGCSCredentials *kms.Secret, currentCryptoPassphrase *kms.Secret) {
// we use the new access secret if plain or empty, otherwise the old value
if user.FsConfig.Provider == dataprovider.S3FilesystemProvider {
if !user.FsConfig.S3Config.AccessSecret.IsPlain() && !user.FsConfig.S3Config.AccessSecret.IsEmpty() {
@@ -227,4 +238,9 @@ func updateEncryptedSecrets(user *dataprovider.User, currentS3AccessSecret, curr
user.FsConfig.GCSConfig.Credentials = currentGCSCredentials
}
}
if user.FsConfig.Provider == dataprovider.CryptedFilesystemProvider {
if !user.FsConfig.CryptConfig.Passphrase.IsPlain() && !user.FsConfig.CryptConfig.Passphrase.IsEmpty() {
user.FsConfig.CryptConfig.Passphrase = currentCryptoPassphrase
}
}
}

View File

@@ -624,6 +624,9 @@ func compareUserFsConfig(expected *dataprovider.User, actual *dataprovider.User)
if err := compareAzBlobConfig(expected, actual); err != nil {
return err
}
if err := checkEncryptedSecret(expected.FsConfig.CryptConfig.Passphrase, actual.FsConfig.CryptConfig.Passphrase); err != nil {
return err
}
return nil
}

View File

@@ -515,6 +515,14 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.AzBlobConfig.UploadPartSize = 101
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u = getTestUser()
u.FsConfig.Provider = dataprovider.CryptedFilesystemProvider
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.CryptConfig.Passphrase = kms.NewSecret(kms.SecretStatusRedacted, "akey", "", "")
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
}
func TestAddUserInvalidVirtualFolders(t *testing.T) {
@@ -1204,6 +1212,7 @@ func TestUserAzureBlobConfig(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
// test user without access key and access secret (sas)
@@ -1229,6 +1238,58 @@ func TestUserAzureBlobConfig(t *testing.T) {
assert.NoError(t, err)
}
func TestUserCryptFs(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
assert.NoError(t, err)
user.FsConfig.Provider = dataprovider.CryptedFilesystemProvider
user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("crypt passphrase")
user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
initialPayload := user.FsConfig.CryptConfig.Passphrase.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
user.FsConfig.CryptConfig.Passphrase.SetStatus(kms.SecretStatusSecretBox)
user.FsConfig.CryptConfig.Passphrase.SetAdditionalData("data")
user.FsConfig.CryptConfig.Passphrase.SetKey("fake pass key")
user, bb, err := httpd.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, initialPayload, user.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
_, err = httpd.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
secret := kms.NewSecret(kms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
user.FsConfig.CryptConfig.Passphrase = secret
_, _, err = httpd.AddUser(user, http.StatusOK)
assert.Error(t, err)
user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("passphrase test")
user, _, err = httpd.AddUser(user, http.StatusOK)
assert.NoError(t, err)
initialPayload = user.FsConfig.CryptConfig.Passphrase.GetPayload()
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
user.FsConfig.Provider = dataprovider.CryptedFilesystemProvider
user.FsConfig.CryptConfig.Passphrase.SetKey("pass")
user, bb, err = httpd.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err, string(bb))
assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, initialPayload)
assert.Equal(t, initialPayload, user.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
_, err = httpd.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
}
func TestUserHiddenFields(t *testing.T) {
err := dataprovider.Close()
assert.NoError(t, err)
@@ -1240,7 +1301,7 @@ func TestUserHiddenFields(t *testing.T) {
assert.NoError(t, err)
// sensitive data must be hidden but not deleted from the dataprovider
usernames := []string{"user1", "user2", "user3"}
usernames := []string{"user1", "user2", "user3", "user4"}
u1 := getTestUser()
u1.Username = usernames[0]
u1.FsConfig.Provider = dataprovider.S3FilesystemProvider
@@ -1268,9 +1329,16 @@ func TestUserHiddenFields(t *testing.T) {
user3, _, err := httpd.AddUser(u3, http.StatusOK)
assert.NoError(t, err)
u4 := getTestUser()
u4.Username = usernames[3]
u4.FsConfig.Provider = dataprovider.CryptedFilesystemProvider
u4.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("test passphrase")
user4, _, err := httpd.AddUser(u4, http.StatusOK)
assert.NoError(t, err)
users, _, err := httpd.GetUsers(0, 0, "", http.StatusOK)
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(users), 3)
assert.GreaterOrEqual(t, len(users), 4)
for _, username := range usernames {
users, _, err = httpd.GetUsers(0, 0, username, http.StatusOK)
assert.NoError(t, err)
@@ -1303,6 +1371,14 @@ func TestUserHiddenFields(t *testing.T) {
assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetStatus())
assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetPayload())
user4, _, err = httpd.GetUserByID(user4.ID, http.StatusOK)
assert.NoError(t, err)
assert.Empty(t, user4.Password)
assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetPayload())
// finally check that we have all the data inside the data provider
user1, err = dataprovider.GetUserByID(user1.ID)
assert.NoError(t, err)
@@ -1346,12 +1422,28 @@ func TestUserHiddenFields(t *testing.T) {
assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetKey())
assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
user4, err = dataprovider.GetUserByID(user4.ID)
assert.NoError(t, err)
assert.NotEmpty(t, user4.Password)
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetPayload())
err = user4.FsConfig.CryptConfig.Passphrase.Decrypt()
assert.NoError(t, err)
assert.Equal(t, kms.SecretStatusPlain, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, u4.FsConfig.CryptConfig.Passphrase.GetPayload(), user4.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
_, err = httpd.RemoveUser(user1, http.StatusOK)
assert.NoError(t, err)
_, err = httpd.RemoveUser(user2, http.StatusOK)
assert.NoError(t, err)
_, err = httpd.RemoveUser(user3, http.StatusOK)
assert.NoError(t, err)
_, err = httpd.RemoveUser(user4, http.StatusOK)
assert.NoError(t, err)
err = dataprovider.Close()
assert.NoError(t, err)
@@ -3410,6 +3502,87 @@ func TestWebUserAzureBlobMock(t *testing.T) {
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestWebUserCryptMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
assert.NoError(t, err)
user.FsConfig.Provider = dataprovider.CryptedFilesystemProvider
user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("crypted passphrase")
form := make(url.Values)
form.Set("username", user.Username)
form.Set("home_dir", user.HomeDir)
form.Set("uid", "0")
form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
form.Set("upload_bandwidth", "0")
form.Set("download_bandwidth", "0")
form.Set("permissions", "*")
form.Set("sub_dirs_permissions", "")
form.Set("status", strconv.Itoa(user.Status))
form.Set("expiration_date", "2020-01-01 00:00:00")
form.Set("allowed_ip", "")
form.Set("denied_ip", "")
form.Set("fs_provider", "4")
form.Set("crypt_passphrase", "")
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir2::.zip")
form.Set("max_upload_file_size", "0")
// passphrase cannot be empty
b, contentType, _ := getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("crypt_passphrase", user.FsConfig.CryptConfig.Passphrase.GetPayload())
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var users []dataprovider.User
err = render.DecodeJSON(rr.Body, &users)
assert.NoError(t, err)
assert.Equal(t, 1, len(users))
updateUser := users[0]
assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
assert.Equal(t, 2, len(updateUser.Filters.FileExtensions))
assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.NotEmpty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
// now check that a redacted password is not saved
form.Set("crypt_passphrase", "[**redacted**] ")
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
users = nil
err = render.DecodeJSON(rr.Body, &users)
assert.NoError(t, err)
assert.Equal(t, 1, len(users))
lastUpdatedUser := users[0]
assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetStatus())
assert.Equal(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload(), lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetPayload())
assert.Empty(t, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetKey())
assert.Empty(t, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestAddWebFoldersMock(t *testing.T) {
mappedPath := filepath.Clean(os.TempDir())
form := make(url.Values)

View File

@@ -389,6 +389,11 @@ func TestCompareUserFsConfig(t *testing.T) {
expected.FsConfig.S3Config.UploadConcurrency = 3
err = compareUserFsConfig(expected, actual)
assert.Error(t, err)
expected.FsConfig.S3Config.UploadConcurrency = 0
expected.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("payload")
err = compareUserFsConfig(expected, actual)
assert.Error(t, err)
expected.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
}
func TestCompareUserGCSConfig(t *testing.T) {

View File

@@ -2,7 +2,7 @@ openapi: 3.0.3
info:
title: SFTPGo
description: 'SFTPGo REST API'
version: 2.1.3
version: 2.2.0
servers:
- url: /api/v1
@@ -1078,6 +1078,12 @@ components:
type: boolean
nullable: true
description: Azure Blob Storage configuration details
CryptFsConfig:
type: object
properties:
passphrase:
$ref: '#/components/schemas/Secret'
description: Crypt filesystem configuration details
FilesystemConfig:
type: object
properties:
@@ -1088,18 +1094,22 @@ components:
- 1
- 2
- 3
- 4
description: >
Providers:
* `0` - Local filesystem
* `1` - S3 Compatible Object Storage
* `2` - Google Cloud Storage
* `3` - Azure Blob Storage
* `4` - Local filesystem encrypted
s3config:
$ref: '#/components/schemas/S3Config'
gcsconfig:
$ref: '#/components/schemas/GCSConfig'
azblobconfig:
$ref: '#/components/schemas/AzureBlobFsConfig'
cryptconfig:
$ref: '#/components/schemas/CryptFsConfig'
description: Storage filesystem details
BaseVirtualFolder:
type: object

View File

@@ -92,8 +92,6 @@ type userPage struct {
RootDirPerms []string
RedactedSecret string
IsAdd bool
IsS3SecretEnc bool
IsAzSecretEnc bool
}
type folderPage struct {
@@ -216,8 +214,6 @@ func renderAddUserPage(w http.ResponseWriter, user dataprovider.User, error stri
ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
ValidProtocols: dataprovider.ValidProtocols,
RootDirPerms: user.GetPermissionsForPath("/"),
IsS3SecretEnc: user.FsConfig.S3Config.AccessSecret.IsEncrypted(),
IsAzSecretEnc: user.FsConfig.AzBlobConfig.AccountKey.IsEncrypted(),
RedactedSecret: redactedSecret,
}
renderTemplate(w, templateUser, data)
@@ -234,8 +230,6 @@ func renderUpdateUserPage(w http.ResponseWriter, user dataprovider.User, error s
ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
ValidProtocols: dataprovider.ValidProtocols,
RootDirPerms: user.GetPermissionsForPath("/"),
IsS3SecretEnc: user.FsConfig.S3Config.AccessSecret.IsEncrypted(),
IsAzSecretEnc: user.FsConfig.AzBlobConfig.AccountKey.IsEncrypted(),
RedactedSecret: redactedSecret,
}
renderTemplate(w, templateUser, data)
@@ -444,6 +438,76 @@ func getSecretFromFormField(r *http.Request, field string) *kms.Secret {
return secret
}
func getS3Config(r *http.Request) (vfs.S3FsConfig, error) {
var err error
config := vfs.S3FsConfig{}
config.Bucket = r.Form.Get("s3_bucket")
config.Region = r.Form.Get("s3_region")
config.AccessKey = r.Form.Get("s3_access_key")
config.AccessSecret = getSecretFromFormField(r, "s3_access_secret")
config.Endpoint = r.Form.Get("s3_endpoint")
config.StorageClass = r.Form.Get("s3_storage_class")
config.KeyPrefix = r.Form.Get("s3_key_prefix")
config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("s3_upload_part_size"), 10, 64)
if err != nil {
return config, err
}
config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("s3_upload_concurrency"))
return config, err
}
func getGCSConfig(r *http.Request) (vfs.GCSFsConfig, error) {
var err error
config := vfs.GCSFsConfig{}
config.Bucket = r.Form.Get("gcs_bucket")
config.StorageClass = r.Form.Get("gcs_storage_class")
config.KeyPrefix = r.Form.Get("gcs_key_prefix")
autoCredentials := r.Form.Get("gcs_auto_credentials")
if autoCredentials != "" {
config.AutomaticCredentials = 1
} else {
config.AutomaticCredentials = 0
}
credentials, _, err := r.FormFile("gcs_credential_file")
if err == http.ErrMissingFile {
return config, nil
}
if err != nil {
return config, err
}
defer credentials.Close()
fileBytes, err := ioutil.ReadAll(credentials)
if err != nil || len(fileBytes) == 0 {
if len(fileBytes) == 0 {
err = errors.New("credentials file size must be greater than 0")
}
return config, err
}
config.Credentials = kms.NewPlainSecret(string(fileBytes))
config.AutomaticCredentials = 0
return config, err
}
func getAzureConfig(r *http.Request) (vfs.AzBlobFsConfig, error) {
var err error
config := vfs.AzBlobFsConfig{}
config.Container = r.Form.Get("az_container")
config.AccountName = r.Form.Get("az_account_name")
config.AccountKey = getSecretFromFormField(r, "az_account_key")
config.SASURL = r.Form.Get("az_sas_url")
config.Endpoint = r.Form.Get("az_endpoint")
config.KeyPrefix = r.Form.Get("az_key_prefix")
config.AccessTier = r.Form.Get("az_access_tier")
config.UseEmulator = len(r.Form.Get("az_use_emulator")) > 0
config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("az_upload_part_size"), 10, 64)
if err != nil {
return config, err
}
config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("az_upload_concurrency"))
return config, err
}
func getFsConfigFromUserPostFields(r *http.Request) (dataprovider.Filesystem, error) {
var fs dataprovider.Filesystem
provider, err := strconv.Atoi(r.Form.Get("fs_provider"))
@@ -452,65 +516,25 @@ func getFsConfigFromUserPostFields(r *http.Request) (dataprovider.Filesystem, er
}
fs.Provider = dataprovider.FilesystemProvider(provider)
if fs.Provider == dataprovider.S3FilesystemProvider {
fs.S3Config.Bucket = r.Form.Get("s3_bucket")
fs.S3Config.Region = r.Form.Get("s3_region")
fs.S3Config.AccessKey = r.Form.Get("s3_access_key")
fs.S3Config.AccessSecret = getSecretFromFormField(r, "s3_access_secret")
fs.S3Config.Endpoint = r.Form.Get("s3_endpoint")
fs.S3Config.StorageClass = r.Form.Get("s3_storage_class")
fs.S3Config.KeyPrefix = r.Form.Get("s3_key_prefix")
fs.S3Config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("s3_upload_part_size"), 10, 64)
if err != nil {
return fs, err
}
fs.S3Config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("s3_upload_concurrency"))
config, err := getS3Config(r)
if err != nil {
return fs, err
}
fs.S3Config = config
} else if fs.Provider == dataprovider.GCSFilesystemProvider {
fs.GCSConfig.Bucket = r.Form.Get("gcs_bucket")
fs.GCSConfig.StorageClass = r.Form.Get("gcs_storage_class")
fs.GCSConfig.KeyPrefix = r.Form.Get("gcs_key_prefix")
autoCredentials := r.Form.Get("gcs_auto_credentials")
if len(autoCredentials) > 0 {
fs.GCSConfig.AutomaticCredentials = 1
} else {
fs.GCSConfig.AutomaticCredentials = 0
}
credentials, _, err := r.FormFile("gcs_credential_file")
if err == http.ErrMissingFile {
return fs, nil
}
config, err := getGCSConfig(r)
if err != nil {
return fs, err
}
defer credentials.Close()
fileBytes, err := ioutil.ReadAll(credentials)
if err != nil || len(fileBytes) == 0 {
if len(fileBytes) == 0 {
err = errors.New("credentials file size must be greater than 0")
}
return fs, err
}
fs.GCSConfig.Credentials = kms.NewPlainSecret(string(fileBytes))
fs.GCSConfig.AutomaticCredentials = 0
fs.GCSConfig = config
} else if fs.Provider == dataprovider.AzureBlobFilesystemProvider {
fs.AzBlobConfig.Container = r.Form.Get("az_container")
fs.AzBlobConfig.AccountName = r.Form.Get("az_account_name")
fs.AzBlobConfig.AccountKey = getSecretFromFormField(r, "az_account_key")
fs.AzBlobConfig.SASURL = r.Form.Get("az_sas_url")
fs.AzBlobConfig.Endpoint = r.Form.Get("az_endpoint")
fs.AzBlobConfig.KeyPrefix = r.Form.Get("az_key_prefix")
fs.AzBlobConfig.AccessTier = r.Form.Get("az_access_tier")
fs.AzBlobConfig.UseEmulator = len(r.Form.Get("az_use_emulator")) > 0
fs.AzBlobConfig.UploadPartSize, err = strconv.ParseInt(r.Form.Get("az_upload_part_size"), 10, 64)
if err != nil {
return fs, err
}
fs.AzBlobConfig.UploadConcurrency, err = strconv.Atoi(r.Form.Get("az_upload_concurrency"))
config, err := getAzureConfig(r)
if err != nil {
return fs, err
}
fs.AzBlobConfig = config
} else if fs.Provider == dataprovider.CryptedFilesystemProvider {
fs.CryptConfig.Passphrase = getSecretFromFormField(r, "crypt_passphrase")
}
return fs, nil
}
@@ -687,6 +711,9 @@ func handleWebUpdateUserPost(w http.ResponseWriter, r *http.Request) {
if !updatedUser.FsConfig.AzBlobConfig.AccountKey.IsPlain() && !updatedUser.FsConfig.AzBlobConfig.AccountKey.IsEmpty() {
updatedUser.FsConfig.AzBlobConfig.AccountKey = user.FsConfig.AzBlobConfig.AccountKey
}
if !updatedUser.FsConfig.CryptConfig.Passphrase.IsPlain() && !updatedUser.FsConfig.CryptConfig.Passphrase.IsEmpty() {
updatedUser.FsConfig.CryptConfig.Passphrase = user.FsConfig.CryptConfig.Passphrase
}
err = dataprovider.UpdateUser(updatedUser)
if err == nil {
if len(r.Form.Get("disconnect")) > 0 {