diff --git a/go.mod b/go.mod index 58c4e8ff..55b0ae6d 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 github.com/aws/aws-sdk-go-v2 v1.17.3 - github.com/aws/aws-sdk-go-v2/config v1.18.6 - github.com/aws/aws-sdk-go-v2/credentials v1.13.6 + github.com/aws/aws-sdk-go-v2/config v1.18.7 + github.com/aws/aws-sdk-go-v2/credentials v1.13.7 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.45 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.46 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.26 github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.10 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 github.com/cockroachdb/cockroach-go/v2 v2.2.19 github.com/coreos/go-oidc/v3 v3.4.0 @@ -92,7 +92,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.27 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -144,7 +144,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.39.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -171,5 +171,5 @@ require ( replace ( github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20221203115213-ba73c775a9fd github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 - golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20221220153730-5f47589cce28 + golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20221223081523-be6917ff6f72 ) diff --git a/go.sum b/go.sum index cd208e7d..64bf6b78 100644 --- a/go.sum +++ b/go.sum @@ -233,17 +233,17 @@ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.3/go.mod h1:gNsR5CaXK github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/config v1.15.15/go.mod h1:A1Lzyy/o21I5/s2FbyX5AevQfSVXpvvIDCoVFD0BC4E= -github.com/aws/aws-sdk-go-v2/config v1.18.6 h1:iSuEAeervBWMHA7Aaq5hCNfwuN2m7x2VuQCnEbbQg68= -github.com/aws/aws-sdk-go-v2/config v1.18.6/go.mod h1:qyjgnyqpKnNGT+C62zMsrZ/Mn2OodYqwIH0DpXiW8f8= +github.com/aws/aws-sdk-go-v2/config v1.18.7 h1:V94lTcix6jouwmAsgQMAEBozVAGJMFhVj+6/++xfe3E= +github.com/aws/aws-sdk-go-v2/config v1.18.7/go.mod h1:OZYsyHFL5PB9UpyS78NElgKs11qI/B5KJau2XOJDXHA= github.com/aws/aws-sdk-go-v2/credentials v1.12.10/go.mod h1:g5eIM5XRs/OzIIK81QMBl+dAuDyoLN0VYaLP+tBqEOk= -github.com/aws/aws-sdk-go-v2/credentials v1.13.6 h1:BXOMvv3O82/4JLggIi67WKlTO56f0rliCKBT4CKyf0o= -github.com/aws/aws-sdk-go-v2/credentials v1.13.6/go.mod h1:VbnUvhw31DUu6aiubViixQwWCBNO/st84dhPeOkmdls= +github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwgZ8fcC8YDIbEwXck= +github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.9/go.mod h1:KDCCm4ONIdHtUloDcFvK2+vshZvx4Zmj7UMDfusuz5s= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.21/go.mod h1:iIYPrQ2rYfZiB/iADYlhj9HHZ9TTi6PqKQPAqygohbE= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.45 h1:ckFtXy51PT613d/KLKPxFiwRqgGIxDhVbNLof6x/XLo= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.45/go.mod h1:xar61xizdVU4pQygvQrNdZY1VCLNcOIvm87KzdZmWrE= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.46 h1:OCX1pQ4pcqhsDV7B92HzdLWjHWOQsILvjLinpaUWhcc= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.46/go.mod h1:MxCBOcyNXGJRvfpPiH+L6n/BF9zbowthGSUZdDvQF/c= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.15/go.mod h1:pWrr2OoHlT7M/Pd2y4HV3gJyPb3qj5qMmnPkKSNPYK4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= @@ -275,14 +275,14 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.27.2/go.mod h1:u+566cosFI+d+motIz3USX github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6 h1:W8pLcSn6Uy0eXgDBUUl8M8Kxv7JCoP68ZKTD04OXLEA= github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6/go.mod h1:L2l2/q76teehcW7YEsgsDjqdsDTERJeX3nOMIFlgGUE= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.14/go.mod h1:xakbH8KMsQQKqzX87uyyzTHshc/0/Df8bsTneTS5pFU= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.10 h1:6obimjQAiRlEUZT7a2Q1ikH7ck4cPO3phGz4wqI5f2w= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.10/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 h1:77V7vnw/NC4DORHVgA97+Ky2p1ri0+ZVYXh6ordUZU0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= github.com/aws/aws-sdk-go-v2/service/sns v1.17.10/go.mod h1:uITsRNVMeCB3MkWpXxXw0eDz8pW4TYLzj+eyQtbhSxM= github.com/aws/aws-sdk-go-v2/service/sqs v1.19.1/go.mod h1:A94o564Gj+Yn+7QO1eLFeI7UVv3riy/YBFOfICVqFvU= github.com/aws/aws-sdk-go-v2/service/ssm v1.27.6/go.mod h1:fiFzQgj4xNOg4/wqmAiPvzgDMXPD+cUEplX/CYn+0j0= github.com/aws/aws-sdk-go-v2/service/sso v1.11.13/go.mod h1:d7ptRksDDgvXaUvxyHZ9SYh+iMDymm94JbVcgvSYSzU= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.27 h1:Nmvn0DJKg00TBmoBweK253Kdsuy4V5Rs68yL/H15uBQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.27/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= github.com/aws/aws-sdk-go-v2/service/sts v1.16.10/go.mod h1:cftkHYN6tCDNfkSasAmclSfl4l7cySoay8vz7p/ce0E= @@ -538,8 +538,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/drakkan/crypto v0.0.0-20221220153730-5f47589cce28 h1:QTEjJpZpyqRFlN4Yh9GIumPHDJ9LYWka9aWRPz1RiOk= -github.com/drakkan/crypto v0.0.0-20221220153730-5f47589cce28/go.mod h1:cy6DFZ6nHFw1bTHZksT/gYKmdxPdzr7Rw7xcJFSayo4= +github.com/drakkan/crypto v0.0.0-20221223081523-be6917ff6f72 h1:Ivant8yrd81A5y3tQOS7vqwL9QaOdlGonHNOfRR3rsQ= +github.com/drakkan/crypto v0.0.0-20221223081523-be6917ff6f72/go.mod h1:cy6DFZ6nHFw1bTHZksT/gYKmdxPdzr7Rw7xcJFSayo4= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= github.com/drakkan/ftpserverlib v0.0.0-20221203115213-ba73c775a9fd h1:wu/ys+33GwD9PyRO8QDCUpI2WBZtwFiDk8QkFPW8rhQ= @@ -1409,8 +1409,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY= github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= diff --git a/internal/httpd/api_admin.go b/internal/httpd/api_admin.go index 26ab4720..845b4f76 100644 --- a/internal/httpd/api_admin.go +++ b/internal/httpd/api_admin.go @@ -116,13 +116,8 @@ func updateAdmin(w http.ResponseWriter, r *http.Request) { return } - adminID := admin.ID - username = admin.Username - totpConfig := admin.Filters.TOTPConfig - recoveryCodes := admin.Filters.RecoveryCodes - admin.Filters.TOTPConfig = dataprovider.AdminTOTPConfig{} - admin.Filters.RecoveryCodes = nil - err = render.DecodeJSON(r.Body, &admin) + var updatedAdmin dataprovider.Admin + err = render.DecodeJSON(r.Body, &updatedAdmin) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return @@ -139,24 +134,28 @@ func updateAdmin(w http.ResponseWriter, r *http.Request) { http.StatusBadRequest) return } - if claims.isCriticalPermRemoved(admin.Permissions) { + if claims.isCriticalPermRemoved(updatedAdmin.Permissions) { sendAPIResponse(w, r, errors.New("you cannot remove these permissions to yourself"), "", http.StatusBadRequest) return } - if admin.Status == 0 { + if updatedAdmin.Status == 0 { sendAPIResponse(w, r, errors.New("you cannot disable yourself"), "", http.StatusBadRequest) return } - if admin.Role != claims.Role { + if updatedAdmin.Role != claims.Role { sendAPIResponse(w, r, errors.New("you cannot add/change your role"), "", http.StatusBadRequest) return } } - admin.ID = adminID - admin.Username = username - admin.Filters.TOTPConfig = totpConfig - admin.Filters.RecoveryCodes = recoveryCodes - if err := dataprovider.UpdateAdmin(&admin, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role); err != nil { + updatedAdmin.ID = admin.ID + updatedAdmin.Username = admin.Username + if updatedAdmin.Password == "" { + updatedAdmin.Password = admin.Password + } + updatedAdmin.Filters.TOTPConfig = admin.Filters.TOTPConfig + updatedAdmin.Filters.RecoveryCodes = admin.Filters.RecoveryCodes + err = dataprovider.UpdateAdmin(&updatedAdmin, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } diff --git a/internal/httpd/api_eventrule.go b/internal/httpd/api_eventrule.go index 0a0af224..7c33deed 100644 --- a/internal/httpd/api_eventrule.go +++ b/internal/httpd/api_eventrule.go @@ -96,32 +96,30 @@ func updateEventAction(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - actionID := action.ID - name = action.Name - currentHTTPPassword := action.Options.HTTPConfig.Password - action.Options = dataprovider.BaseEventActionOptions{} - err = render.DecodeJSON(r.Body, &action) + var updatedAction dataprovider.BaseEventAction + err = render.DecodeJSON(r.Body, &updatedAction) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - action.ID = actionID - action.Name = name - action.Options.SetEmptySecretsIfNil() - switch action.Type { + updatedAction.ID = action.ID + updatedAction.Name = action.Name + updatedAction.Options.SetEmptySecretsIfNil() + + switch updatedAction.Type { case dataprovider.ActionTypeHTTP: - if action.Options.HTTPConfig.Password.IsNotPlainAndNotEmpty() { - action.Options.HTTPConfig.Password = currentHTTPPassword + if updatedAction.Options.HTTPConfig.Password.IsNotPlainAndNotEmpty() { + updatedAction.Options.HTTPConfig.Password = action.Options.HTTPConfig.Password } } - err = dataprovider.UpdateEventAction(&action, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + err = dataprovider.UpdateEventAction(&updatedAction, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - sendAPIResponse(w, r, nil, "Event target updated", http.StatusOK) + sendAPIResponse(w, r, nil, "Event action updated", http.StatusOK) } func deleteEventAction(w http.ResponseWriter, r *http.Request) { @@ -206,25 +204,22 @@ func updateEventRule(w http.ResponseWriter, r *http.Request) { return } - name := getURLParam(r, "name") - rule, err := dataprovider.EventRuleExists(name) + rule, err := dataprovider.EventRuleExists(getURLParam(r, "name")) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - ruleID := rule.ID - name = rule.Name - rule.Actions = nil - err = render.DecodeJSON(r.Body, &rule) + var updatedRule dataprovider.EventRule + err = render.DecodeJSON(r.Body, &updatedRule) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - rule.ID = ruleID - rule.Name = name + updatedRule.ID = rule.ID + updatedRule.Name = rule.Name - err = dataprovider.UpdateEventRule(&rule, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + err = dataprovider.UpdateEventRule(&updatedRule, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return diff --git a/internal/httpd/api_folder.go b/internal/httpd/api_folder.go index 9e8bf40e..0f522fe0 100644 --- a/internal/httpd/api_folder.go +++ b/internal/httpd/api_folder.go @@ -76,39 +76,23 @@ func updateFolder(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - users := folder.Users - groups := folder.Groups - folderID := folder.ID - name = folder.Name - currentS3AccessSecret := folder.FsConfig.S3Config.AccessSecret - currentAzAccountKey := folder.FsConfig.AzBlobConfig.AccountKey - currentAzSASUrl := folder.FsConfig.AzBlobConfig.SASURL - currentGCSCredentials := folder.FsConfig.GCSConfig.Credentials - currentCryptoPassphrase := folder.FsConfig.CryptConfig.Passphrase - currentSFTPPassword := folder.FsConfig.SFTPConfig.Password - currentSFTPKey := folder.FsConfig.SFTPConfig.PrivateKey - currentSFTPKeyPassphrase := folder.FsConfig.SFTPConfig.KeyPassphrase - currentHTTPPassword := folder.FsConfig.HTTPConfig.Password - currentHTTPAPIKey := folder.FsConfig.HTTPConfig.APIKey - folder.FsConfig.S3Config = vfs.S3FsConfig{} - folder.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{} - folder.FsConfig.GCSConfig = vfs.GCSFsConfig{} - folder.FsConfig.CryptConfig = vfs.CryptFsConfig{} - folder.FsConfig.SFTPConfig = vfs.SFTPFsConfig{} - folder.FsConfig.HTTPConfig = vfs.HTTPFsConfig{} - err = render.DecodeJSON(r.Body, &folder) + var updatedFolder vfs.BaseVirtualFolder + err = render.DecodeJSON(r.Body, &updatedFolder) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - folder.ID = folderID - folder.Name = name - folder.FsConfig.SetEmptySecretsIfNil() - updateEncryptedSecrets(&folder.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl, currentGCSCredentials, - currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey, currentSFTPKeyPassphrase, currentHTTPPassword, - currentHTTPAPIKey) - err = dataprovider.UpdateFolder(&folder, users, groups, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + updatedFolder.ID = folder.ID + updatedFolder.Name = folder.Name + updatedFolder.FsConfig.SetEmptySecretsIfNil() + updateEncryptedSecrets(&updatedFolder.FsConfig, folder.FsConfig.S3Config.AccessSecret, folder.FsConfig.AzBlobConfig.AccountKey, + folder.FsConfig.AzBlobConfig.SASURL, folder.FsConfig.GCSConfig.Credentials, folder.FsConfig.CryptConfig.Passphrase, + folder.FsConfig.SFTPConfig.Password, folder.FsConfig.SFTPConfig.PrivateKey, folder.FsConfig.SFTPConfig.KeyPassphrase, + folder.FsConfig.HTTPConfig.Password, folder.FsConfig.HTTPConfig.APIKey) + + err = dataprovider.UpdateFolder(&updatedFolder, folder.Users, folder.Groups, claims.Username, + util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return diff --git a/internal/httpd/api_group.go b/internal/httpd/api_group.go index a994eea4..15187709 100644 --- a/internal/httpd/api_group.go +++ b/internal/httpd/api_group.go @@ -22,7 +22,6 @@ import ( "github.com/drakkan/sftpgo/v2/internal/dataprovider" "github.com/drakkan/sftpgo/v2/internal/util" - "github.com/drakkan/sftpgo/v2/internal/vfs" ) func getGroups(w http.ResponseWriter, r *http.Request) { @@ -76,9 +75,7 @@ func updateGroup(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - users := group.Users - groupID := group.ID - name = group.Name + currentS3AccessSecret := group.UserSettings.FsConfig.S3Config.AccessSecret currentAzAccountKey := group.UserSettings.FsConfig.AzBlobConfig.AccountKey currentAzSASUrl := group.UserSettings.FsConfig.AzBlobConfig.SASURL @@ -90,24 +87,20 @@ func updateGroup(w http.ResponseWriter, r *http.Request) { currentHTTPPassword := group.UserSettings.FsConfig.HTTPConfig.Password currentHTTPAPIKey := group.UserSettings.FsConfig.HTTPConfig.APIKey - group.UserSettings.FsConfig.S3Config = vfs.S3FsConfig{} - group.UserSettings.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{} - group.UserSettings.FsConfig.GCSConfig = vfs.GCSFsConfig{} - group.UserSettings.FsConfig.CryptConfig = vfs.CryptFsConfig{} - group.UserSettings.FsConfig.SFTPConfig = vfs.SFTPFsConfig{} - group.UserSettings.FsConfig.HTTPConfig = vfs.HTTPFsConfig{} - err = render.DecodeJSON(r.Body, &group) + var updatedGroup dataprovider.Group + err = render.DecodeJSON(r.Body, &updatedGroup) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - group.ID = groupID - group.Name = name - group.UserSettings.FsConfig.SetEmptySecretsIfNil() - updateEncryptedSecrets(&group.UserSettings.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl, + updatedGroup.ID = group.ID + updatedGroup.Name = group.Name + updatedGroup.UserSettings.FsConfig.SetEmptySecretsIfNil() + updateEncryptedSecrets(&updatedGroup.UserSettings.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl, currentGCSCredentials, currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey, currentSFTPKeyPassphrase, currentHTTPPassword, currentHTTPAPIKey) - err = dataprovider.UpdateGroup(&group, users, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + err = dataprovider.UpdateGroup(&updatedGroup, group.Users, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), + claims.Role) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return diff --git a/internal/httpd/api_keys.go b/internal/httpd/api_keys.go index e32181a2..fbb249c4 100644 --- a/internal/httpd/api_keys.go +++ b/internal/httpd/api_keys.go @@ -98,14 +98,17 @@ func updateAPIKey(w http.ResponseWriter, r *http.Request) { return } - err = render.DecodeJSON(r.Body, &apiKey) + var updatedAPIKey dataprovider.APIKey + err = render.DecodeJSON(r.Body, &updatedAPIKey) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - apiKey.KeyID = keyID - if err := dataprovider.UpdateAPIKey(&apiKey, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role); err != nil { + updatedAPIKey.KeyID = keyID + updatedAPIKey.Key = apiKey.Key + err = dataprovider.UpdateAPIKey(&updatedAPIKey, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } diff --git a/internal/httpd/api_role.go b/internal/httpd/api_role.go index 9976d7e1..70678bbd 100644 --- a/internal/httpd/api_role.go +++ b/internal/httpd/api_role.go @@ -75,16 +75,17 @@ func updateRole(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - roleID := role.ID - name = role.Name - err = render.DecodeJSON(r.Body, &role) + + var updatedRole dataprovider.Role + err = render.DecodeJSON(r.Body, &updatedRole) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - role.ID = roleID - role.Name = name - err = dataprovider.UpdateRole(&role, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + + updatedRole.ID = role.ID + updatedRole.Name = role.Name + err = dataprovider.UpdateRole(&updatedRole, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return diff --git a/internal/httpd/api_shares.go b/internal/httpd/api_shares.go index e98d8f52..5d7de68f 100644 --- a/internal/httpd/api_shares.go +++ b/internal/httpd/api_shares.go @@ -131,26 +131,27 @@ func updateShare(w http.ResponseWriter, r *http.Request) { return } - oldPassword := share.Password - err = render.DecodeJSON(r.Body, &share) + var updatedShare dataprovider.Share + err = render.DecodeJSON(r.Body, &updatedShare) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - share.ShareID = shareID - share.Username = claims.Username - if share.Password == redactedSecret { - share.Password = oldPassword + updatedShare.ShareID = shareID + updatedShare.Username = claims.Username + if updatedShare.Password == redactedSecret { + updatedShare.Password = share.Password } - if share.Password == "" { + if updatedShare.Password == "" { if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) { sendAPIResponse(w, r, nil, "You are not authorized to share files/folders without a password", http.StatusForbidden) return } } - if err := dataprovider.UpdateShare(&share, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role); err != nil { + err = dataprovider.UpdateShare(&updatedShare, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } diff --git a/internal/httpd/api_user.go b/internal/httpd/api_user.go index 4c23ba58..dff96a24 100644 --- a/internal/httpd/api_user.go +++ b/internal/httpd/api_user.go @@ -163,55 +163,28 @@ func updateUser(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) return } - userID := user.ID - username = user.Username - lastPwdChange := user.LastPasswordChange - totpConfig := user.Filters.TOTPConfig - recoveryCodes := user.Filters.RecoveryCodes - currentPermissions := user.Permissions - currentS3AccessSecret := user.FsConfig.S3Config.AccessSecret - currentAzAccountKey := user.FsConfig.AzBlobConfig.AccountKey - currentAzSASUrl := user.FsConfig.AzBlobConfig.SASURL - currentGCSCredentials := user.FsConfig.GCSConfig.Credentials - currentCryptoPassphrase := user.FsConfig.CryptConfig.Passphrase - currentSFTPPassword := user.FsConfig.SFTPConfig.Password - currentSFTPKey := user.FsConfig.SFTPConfig.PrivateKey - currentSFTPKeyPassphrase := user.FsConfig.SFTPConfig.KeyPassphrase - currentHTTPPassword := user.FsConfig.HTTPConfig.Password - currentHTTPAPIKey := user.FsConfig.HTTPConfig.APIKey - 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{} - user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{} - user.FsConfig.HTTPConfig = vfs.HTTPFsConfig{} - user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{} - user.Filters.RecoveryCodes = nil - user.VirtualFolders = nil - err = render.DecodeJSON(r.Body, &user) + var updatedUser dataprovider.User + updatedUser.Password = user.Password + err = render.DecodeJSON(r.Body, &updatedUser) if err != nil { sendAPIResponse(w, r, err, "", http.StatusBadRequest) return } - user.ID = userID - user.Username = username - user.Filters.TOTPConfig = totpConfig - user.Filters.RecoveryCodes = recoveryCodes - user.LastPasswordChange = lastPwdChange - user.SetEmptySecretsIfNil() - // we use new Permissions if passed otherwise the old ones - if len(user.Permissions) == 0 { - user.Permissions = currentPermissions - } - updateEncryptedSecrets(&user.FsConfig, currentS3AccessSecret, currentAzAccountKey, currentAzSASUrl, - currentGCSCredentials, currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey, currentSFTPKeyPassphrase, - currentHTTPPassword, currentHTTPAPIKey) + updatedUser.ID = user.ID + updatedUser.Username = user.Username + updatedUser.Filters.RecoveryCodes = user.Filters.RecoveryCodes + updatedUser.Filters.TOTPConfig = user.Filters.TOTPConfig + updatedUser.LastPasswordChange = user.LastPasswordChange + updatedUser.SetEmptySecretsIfNil() + updateEncryptedSecrets(&updatedUser.FsConfig, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey, + user.FsConfig.AzBlobConfig.SASURL, user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase, + user.FsConfig.SFTPConfig.Password, user.FsConfig.SFTPConfig.PrivateKey, user.FsConfig.SFTPConfig.KeyPassphrase, + user.FsConfig.HTTPConfig.Password, user.FsConfig.HTTPConfig.APIKey) if claims.Role != "" { - user.Role = claims.Role + updatedUser.Role = claims.Role } - err = dataprovider.UpdateUser(&user, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) + err = dataprovider.UpdateUser(&updatedUser, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role) if err != nil { sendAPIResponse(w, r, err, "", getRespStatus(err)) return diff --git a/internal/httpd/httpd_test.go b/internal/httpd/httpd_test.go index 8a8ecc02..08112b0e 100644 --- a/internal/httpd/httpd_test.go +++ b/internal/httpd/httpd_test.go @@ -668,8 +668,8 @@ func TestRoleRelations(t *testing.T) { admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated) assert.NoError(t, err) admin.Role = "invalid role" - _, _, err = httpdtest.UpdateAdmin(admin, http.StatusInternalServerError) - assert.NoError(t, err) + _, resp, err = httpdtest.UpdateAdmin(admin, http.StatusInternalServerError) + assert.NoError(t, err, string(resp)) admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK) assert.NoError(t, err) assert.Equal(t, role.Name, admin.Role) @@ -813,21 +813,38 @@ func TestBasicGroupHandling(t *testing.T) { group.UserSettings.FsConfig.SFTPConfig.Endpoint = sftpServerAddr group.UserSettings.FsConfig.SFTPConfig.Username = defaultUsername group.UserSettings.FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword) + group.UserSettings.Permissions = map[string][]string{ + "/": {dataprovider.PermAny}, + } + group.UserSettings.Filters.AllowedIP = []string{"10.0.0.0/8"} group, _, err = httpdtest.UpdateGroup(group, http.StatusOK) assert.NoError(t, err) + groupGet, _, err = httpdtest.GetGroupByName(group.Name, http.StatusOK) + assert.NoError(t, err) + assert.Len(t, groupGet.UserSettings.Permissions, 1) + assert.Len(t, groupGet.UserSettings.Filters.AllowedIP, 1) + // update again and check that the password was preserved dbGroup, err := dataprovider.GroupExists(group.Name) assert.NoError(t, err) group.UserSettings.FsConfig.SFTPConfig.Password = kms.NewSecret( dbGroup.UserSettings.FsConfig.SFTPConfig.Password.GetStatus(), dbGroup.UserSettings.FsConfig.SFTPConfig.Password.GetPayload(), "", "") + group.UserSettings.Permissions = nil + group.UserSettings.Filters.AllowedIP = nil group, _, err = httpdtest.UpdateGroup(group, http.StatusOK) assert.NoError(t, err) + assert.Len(t, group.UserSettings.Permissions, 0) + assert.Len(t, group.UserSettings.Filters.AllowedIP, 0) dbGroup, err = dataprovider.GroupExists(group.Name) assert.NoError(t, err) err = dbGroup.UserSettings.FsConfig.SFTPConfig.Password.Decrypt() assert.NoError(t, err) assert.Equal(t, defaultPassword, dbGroup.UserSettings.FsConfig.SFTPConfig.Password.GetPayload()) + // check the group permissions + groupGet, _, err = httpdtest.GetGroupByName(group.Name, http.StatusOK) + assert.NoError(t, err) + assert.Len(t, groupGet.UserSettings.Permissions, 0) group.UserSettings.HomeDir = "relative path" _, _, err = httpdtest.UpdateGroup(group, http.StatusBadRequest) @@ -4191,7 +4208,11 @@ func TestUpdateUserEmptyPassword(t *testing.T) { assert.NotEmpty(t, dbUser.Password) assert.True(t, dbUser.IsPasswordHashed()) // now update the user and set an empty password - customUser := make(map[string]any) + data, err := json.Marshal(dbUser) + assert.NoError(t, err) + var customUser map[string]any + err = json.Unmarshal(data, &customUser) + assert.NoError(t, err) customUser["password"] = "" asJSON, err := json.Marshal(customUser) assert.NoError(t, err) @@ -4208,6 +4229,30 @@ func TestUpdateUserEmptyPassword(t *testing.T) { assert.NoError(t, err) } +func TestUpdateUserNoPassword(t *testing.T) { + u := getTestUser() + u.PublicKeys = []string{testPubKey} + user, _, err := httpdtest.AddUser(u, http.StatusCreated) + assert.NoError(t, err) + // the password is not empty + dbUser, err := dataprovider.UserExists(u.Username, "") + assert.NoError(t, err) + assert.NotEmpty(t, dbUser.Password) + assert.True(t, dbUser.IsPasswordHashed()) + // now update the user and remove the password field, old password should be preserved + user.Password = "" // password has the omitempty tag + _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") + assert.NoError(t, err) + // the password is preserved + dbUser, err = dataprovider.UserExists(u.Username, "") + assert.NoError(t, err) + assert.NotEmpty(t, dbUser.Password) + assert.True(t, dbUser.IsPasswordHashed()) + + _, err = httpdtest.RemoveUser(user, http.StatusOK) + assert.NoError(t, err) +} + func TestUpdateUser(t *testing.T) { u := getTestUser() u.UsedQuotaFiles = 1 @@ -5947,10 +5992,11 @@ func TestNamingRules(t *testing.T) { assert.Equal(t, "文件夹ab", folder.Name) folder.Name = f.Name folder.Description = folder.Name - folder, resp, err = httpdtest.UpdateFolder(folder, http.StatusOK) + _, resp, err = httpdtest.UpdateFolder(folder, http.StatusOK) assert.NoError(t, err, string(resp)) folder, resp, err = httpdtest.GetFolderByName(f.Name, http.StatusOK) assert.NoError(t, err, string(resp)) + assert.Equal(t, "文件夹AB", folder.Description) _, err = httpdtest.RemoveFolder(f, http.StatusOK) assert.NoError(t, err) token, err := getJWTWebClientTokenFromTestServer(u.Username, defaultPassword) @@ -9620,7 +9666,10 @@ func TestWebAPIChangeUserProfileMock(t *testing.T) { assert.Equal(t, email, profileReq["email"].(string)) assert.Equal(t, description, profileReq["description"].(string)) assert.True(t, profileReq["allow_api_key_auth"].(bool)) - assert.Len(t, profileReq["public_keys"].([]any), 2) + val, ok := profileReq["public_keys"].([]any) + if assert.True(t, ok, profileReq) { + assert.Len(t, val, 2) + } // set an invalid email profileReq = make(map[string]any) profileReq["email"] = "notavalidemail" @@ -9644,6 +9693,8 @@ func TestWebAPIChangeUserProfileMock(t *testing.T) { checkResponseCode(t, http.StatusBadRequest, rr) assert.Contains(t, rr.Body.String(), "Validation error: could not parse key") + user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) + assert.NoError(t, err) user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientPubKeyChangeDisabled} user.Email = email user.Description = description @@ -9678,8 +9729,13 @@ func TestWebAPIChangeUserProfileMock(t *testing.T) { assert.Equal(t, email, profileReq["email"].(string)) assert.Equal(t, description+"_mod", profileReq["description"].(string)) assert.True(t, profileReq["allow_api_key_auth"].(bool)) - assert.Len(t, profileReq["public_keys"].([]any), 2) + val, ok = profileReq["public_keys"].([]any) + if assert.True(t, ok, profileReq) { + assert.Len(t, val, 2) + } + user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) + assert.NoError(t, err) user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientInfoChangeDisabled} user.Description = description + "_mod" _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") @@ -10299,47 +10355,6 @@ func TestUserHandlingWithAPIKey(t *testing.T) { assert.NoError(t, err) } -func TestUpdateUserMock(t *testing.T) { - token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) - assert.NoError(t, err) - user := getTestUser() - userAsJSON := getUserAsJSON(t, user) - req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON)) - setBearerForReq(req, token) - rr := executeRequest(req) - checkResponseCode(t, http.StatusCreated, rr) - err = render.DecodeJSON(rr.Body, &user) - assert.NoError(t, err) - // permissions should not change if empty or nil - permissions := user.Permissions - user.Permissions = make(map[string][]string) - userAsJSON = getUserAsJSON(t, user) - req, _ = http.NewRequest(http.MethodPut, userPath+"/"+user.Username, bytes.NewBuffer(userAsJSON)) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) - req, _ = http.NewRequest(http.MethodGet, userPath+"/"+user.Username, nil) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) - var updatedUser dataprovider.User - err = render.DecodeJSON(rr.Body, &updatedUser) - assert.NoError(t, err) - for dir, perms := range permissions { - if actualPerms, ok := updatedUser.Permissions[dir]; ok { - for _, v := range actualPerms { - assert.True(t, util.Contains(perms, v)) - } - } else { - assert.Fail(t, "Permissions directories mismatch") - } - } - req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) -} - func TestUpdateUserQuotaUsageMock(t *testing.T) { token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) assert.NoError(t, err) diff --git a/internal/sftpd/sftpd_test.go b/internal/sftpd/sftpd_test.go index 08fc2831..5fcfe9b9 100644 --- a/internal/sftpd/sftpd_test.go +++ b/internal/sftpd/sftpd_test.go @@ -2668,29 +2668,7 @@ func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) { user, _, err := httpdtest.AddUser(getTestUser(usePubKey), http.StatusCreated) assert.NoError(t, err) user.Password = "" - user.PublicKeys = []string{} - // password and public key should remain unchanged - _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") - assert.NoError(t, err) - conn, client, err := getSftpClient(user, usePubKey) - if assert.NoError(t, err) { - defer conn.Close() - defer client.Close() - assert.NoError(t, checkBasicSFTP(client)) - } - _, err = httpdtest.RemoveUser(user, http.StatusOK) - assert.NoError(t, err) - err = os.RemoveAll(user.GetHomeDir()) - assert.NoError(t, err) -} - -func TestLoginAfterUserUpdateEmptyPubKey(t *testing.T) { - usePubKey := true - user, _, err := httpdtest.AddUser(getTestUser(usePubKey), http.StatusCreated) - assert.NoError(t, err) - user.Password = "" - user.PublicKeys = []string{} - // password and public key should remain unchanged + // password should remain unchanged _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") assert.NoError(t, err) conn, client, err := getSftpClient(user, usePubKey)