diff --git a/README.md b/README.md index cf74fae1..146eb596 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SFTPGo -![CI Status](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push) +[![CI Status](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push) [![Code Coverage](https://codecov.io/gh/drakkan/sftpgo/branch/main/graph/badge.svg)](https://codecov.io/gh/drakkan/sftpgo/branch/main) [![License: AGPL v3](https://img.shields.io/badge/License-AGPLv3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) [![Docker Pulls](https://img.shields.io/docker/pulls/drakkan/sftpgo)](https://hub.docker.com/r/drakkan/sftpgo) diff --git a/README.zh_CN.md b/README.zh_CN.md index b0f38c8d..c74dee51 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -1,6 +1,6 @@ # SFTPGo -![CI Status](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push) +[![CI Status](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push) [![Code Coverage](https://codecov.io/gh/drakkan/sftpgo/branch/main/graph/badge.svg)](https://codecov.io/gh/drakkan/sftpgo/branch/main) [![License: AGPL v3](https://img.shields.io/badge/License-AGPLv3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) [![Docker Pulls](https://img.shields.io/docker/pulls/drakkan/sftpgo)](https://hub.docker.com/r/drakkan/sftpgo) diff --git a/go.mod b/go.mod index 91c2f497..48863907 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.26.11 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.10 github.com/aws/aws-sdk-go-v2/service/sts v1.16.7 - github.com/cockroachdb/cockroach-go/v2 v2.2.12 + github.com/cockroachdb/cockroach-go/v2 v2.2.13 github.com/coreos/go-oidc/v3 v3.2.0 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 github.com/fclairamb/ftpserverlib v0.18.1-0.20220515214847-f96d31ec626e @@ -154,7 +154,7 @@ require ( golang.org/x/tools v0.1.11 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac // indirect + google.golang.org/genproto v0.0.0-20220614154056-d2c91c45c995 // indirect google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/ini.v1 v1.66.6 // indirect diff --git a/go.sum b/go.sum index 0b086305..1df09575 100644 --- a/go.sum +++ b/go.sum @@ -229,8 +229,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.2.12 h1:yGneJ5OvdtAky2nD5BKTKmIqrcUlbrEIJ1ILHirnn3o= -github.com/cockroachdb/cockroach-go/v2 v2.2.12/go.mod h1:xZ2VHjUEb/cySv0scXBx7YsBnHtLHkR1+w/w73b5i3M= +github.com/cockroachdb/cockroach-go/v2 v2.2.13 h1:IsQmOtHQrfv0v3AqIEv+mStB09amzbH8gDDyVcpl3xQ= +github.com/cockroachdb/cockroach-go/v2 v2.2.13/go.mod h1:xZ2VHjUEb/cySv0scXBx7YsBnHtLHkR1+w/w73b5i3M= github.com/coreos/go-oidc/v3 v3.2.0 h1:2eR2MGR7thBXSQ2YbODlF0fcmgtliLCfr9iX6RW11fc= github.com/coreos/go-oidc/v3 v3.2.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1202,8 +1202,8 @@ google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac h1:ByeiW1F67iV9o8ipGskA+HWzSkMbRJuKLlwCdPxzn7A= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220614154056-d2c91c45c995 h1:RJSqnopW/SLDXggSc2Psf704BMQ0Yz7AE6fjhQ62qYI= +google.golang.org/genproto v0.0.0-20220614154056-d2c91c45c995/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/httpd/api_defender.go b/httpd/api_defender.go index 32c0bf02..83fe7e67 100644 --- a/httpd/api_defender.go +++ b/httpd/api_defender.go @@ -6,13 +6,11 @@ import ( "fmt" "net" "net/http" - "time" "github.com/go-chi/render" "github.com/drakkan/sftpgo/v2/common" "github.com/drakkan/sftpgo/v2/dataprovider" - "github.com/drakkan/sftpgo/v2/util" ) func getDefenderHosts(w http.ResponseWriter, r *http.Request) { @@ -59,85 +57,6 @@ func deleteDefenderHostByID(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, nil, "OK", http.StatusOK) } -func getBanTime(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - ip := r.URL.Query().Get("ip") - err := validateIPAddress(ip) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - - banStatus := make(map[string]*string) - - banTime, err := common.GetDefenderBanTime(ip) - if err != nil { - if _, ok := err.(*util.RecordNotFoundError); ok { - banTime = nil - } else { - sendAPIResponse(w, r, err, "", getRespStatus(err)) - return - } - } - var banTimeString *string - if banTime != nil { - rfc3339String := banTime.UTC().Format(time.RFC3339) - banTimeString = &rfc3339String - } - - banStatus["date_time"] = banTimeString - render.JSON(w, r, banStatus) -} - -func getScore(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - ip := r.URL.Query().Get("ip") - err := validateIPAddress(ip) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - - score, err := common.GetDefenderScore(ip) - if err != nil { - if _, ok := err.(*util.RecordNotFoundError); ok { - score = 0 - } else { - sendAPIResponse(w, r, err, "", getRespStatus(err)) - return - } - } - - scoreStatus := make(map[string]int) - scoreStatus["score"] = score - - render.JSON(w, r, scoreStatus) -} - -func unban(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - - var postBody map[string]string - err := render.DecodeJSON(r.Body, &postBody) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - - ip := postBody["ip"] - err = validateIPAddress(ip) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - - if common.DeleteDefenderHost(ip) { - sendAPIResponse(w, r, nil, "OK", http.StatusOK) - } else { - sendAPIResponse(w, r, nil, "Not found", http.StatusNotFound) - } -} - func getIPFromID(r *http.Request) (string, error) { decoded, err := hex.DecodeString(getURLParam(r, "id")) if err != nil { @@ -152,9 +71,6 @@ func getIPFromID(r *http.Request) (string, error) { } func validateIPAddress(ip string) error { - if ip == "" { - return errors.New("ip address is required") - } if net.ParseIP(ip) == nil { return fmt.Errorf("ip address %#v is not valid", ip) } diff --git a/httpd/api_http_user.go b/httpd/api_http_user.go index 4fe3c23e..4b061ec5 100644 --- a/httpd/api_http_user.go +++ b/httpd/api_http_user.go @@ -380,51 +380,6 @@ func getUserFilesAsZipStream(w http.ResponseWriter, r *http.Request) { renderCompressedFiles(w, connection, baseDir, filesList, nil) } -func getUserPublicKeys(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - claims, err := getTokenClaims(r) - if err != nil || claims.Username == "" { - sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest) - return - } - user, err := dataprovider.UserExists(claims.Username) - if err != nil { - sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err)) - return - } - render.JSON(w, r, user.PublicKeys) -} - -func setUserPublicKeys(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - - claims, err := getTokenClaims(r) - if err != nil || claims.Username == "" { - sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest) - return - } - user, err := dataprovider.UserExists(claims.Username) - if err != nil { - sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err)) - return - } - - var publicKeys []string - err = render.DecodeJSON(r.Body, &publicKeys) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - - user.PublicKeys = publicKeys - err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr)) - if err != nil { - sendAPIResponse(w, r, err, "", getRespStatus(err)) - return - } - sendAPIResponse(w, r, err, "Public keys updated", http.StatusOK) -} - func getUserProfile(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) claims, err := getTokenClaims(r) diff --git a/httpd/api_quota.go b/httpd/api_quota.go index d1edece3..cc919162 100644 --- a/httpd/api_quota.go +++ b/httpd/api_quota.go @@ -49,22 +49,6 @@ func updateUserQuotaUsage(w http.ResponseWriter, r *http.Request) { doUpdateUserQuotaUsage(w, r, getURLParam(r, "username"), usage) } -func updateUserQuotaUsageCompat(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - var u dataprovider.User - err := render.DecodeJSON(r.Body, &u) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - usage := quotaUsage{ - UsedQuotaSize: u.UsedQuotaSize, - UsedQuotaFiles: u.UsedQuotaFiles, - } - - doUpdateUserQuotaUsage(w, r, u.Username, usage) -} - func updateFolderQuotaUsage(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) var usage quotaUsage @@ -76,53 +60,16 @@ func updateFolderQuotaUsage(w http.ResponseWriter, r *http.Request) { doUpdateFolderQuotaUsage(w, r, getURLParam(r, "name"), usage) } -func updateFolderQuotaUsageCompat(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - var f vfs.BaseVirtualFolder - err := render.DecodeJSON(r.Body, &f) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - usage := quotaUsage{ - UsedQuotaSize: f.UsedQuotaSize, - UsedQuotaFiles: f.UsedQuotaFiles, - } - doUpdateFolderQuotaUsage(w, r, f.Name, usage) -} - func startUserQuotaScan(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) doStartUserQuotaScan(w, r, getURLParam(r, "username")) } -func startUserQuotaScanCompat(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - var u dataprovider.User - err := render.DecodeJSON(r.Body, &u) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - doStartUserQuotaScan(w, r, u.Username) -} - func startFolderQuotaScan(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) doStartFolderQuotaScan(w, r, getURLParam(r, "name")) } -func startFolderQuotaScanCompat(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - var f vfs.BaseVirtualFolder - err := render.DecodeJSON(r.Body, &f) - if err != nil { - sendAPIResponse(w, r, err, "", http.StatusBadRequest) - return - } - doStartFolderQuotaScan(w, r, f.Name) -} - func updateUserTransferQuotaUsage(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) var usage transferQuotaUsage diff --git a/httpd/httpd.go b/httpd/httpd.go index 8bc5eaab..1f25782c 100644 --- a/httpd/httpd.go +++ b/httpd/httpd.go @@ -37,8 +37,6 @@ const ( userLogoutPath = "/api/v2/user/logout" activeConnectionsPath = "/api/v2/connections" quotasBasePath = "/api/v2/quotas" - quotaScanPath = "/api/v2/quota-scans" - quotaScanVFolderPath = "/api/v2/folder-quota-scans" userPath = "/api/v2/users" versionPath = "/api/v2/version" folderPath = "/api/v2/folders" @@ -46,21 +44,12 @@ const ( serverStatusPath = "/api/v2/status" dumpDataPath = "/api/v2/dumpdata" loadDataPath = "/api/v2/loaddata" - updateUsedQuotaPath = "/api/v2/quota-update" - updateFolderUsedQuotaPath = "/api/v2/folder-quota-update" defenderHosts = "/api/v2/defender/hosts" - defenderBanTime = "/api/v2/defender/bantime" - defenderUnban = "/api/v2/defender/unban" - defenderScore = "/api/v2/defender/score" adminPath = "/api/v2/admins" adminPwdPath = "/api/v2/admin/changepwd" - adminPwdCompatPath = "/api/v2/changepwd/admin" adminProfilePath = "/api/v2/admin/profile" userPwdPath = "/api/v2/user/changepwd" - userPublicKeysPath = "/api/v2/user/publickeys" - userFolderPath = "/api/v2/user/folder" userDirsPath = "/api/v2/user/dirs" - userFilePath = "/api/v2/user/file" userFilesPath = "/api/v2/user/files" userStreamZipPath = "/api/v2/user/streamzip" userUploadFilePath = "/api/v2/user/files/upload" diff --git a/httpd/httpd_test.go b/httpd/httpd_test.go index 85e0112a..cd93cf7a 100644 --- a/httpd/httpd_test.go +++ b/httpd/httpd_test.go @@ -59,119 +59,111 @@ import ( ) const ( - defaultUsername = "test_user" - defaultPassword = "test_password" - testPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1" - testPubKey1 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCd60+/j+y8f0tLftihWV1YN9RSahMI9btQMDIMqts/jeNbD8jgoogM3nhF7KxfcaMKURuD47KC4Ey6iAJUJ0sWkSNNxOcIYuvA+5MlspfZDsa8Ag76Fe1vyz72WeHMHMeh/hwFo2TeIeIXg480T1VI6mzfDrVp2GzUx0SS0dMsQBjftXkuVR8YOiOwMCAH2a//M1OrvV7d/NBk6kBN0WnuIBb2jKm15PAA7+jQQG7tzwk2HedNH3jeL5GH31xkSRwlBczRK0xsCQXehAlx6cT/e/s44iJcJTHfpPKoSk6UAhPJYe7Z1QnuoawY9P9jQaxpyeImBZxxUEowhjpj2avBxKdRGBVK8R7EL8tSOeLbhdyWe5Mwc1+foEbq9Zz5j5Kd+hn3Wm1UnsGCrXUUUoZp1jnlNl0NakCto+5KmqnT9cHxaY+ix2RLUWAZyVFlRq71OYux1UHJnEJPiEI1/tr4jFBSL46qhQZv/TfpkfVW8FLz0lErfqu0gQEZnNHr3Fc= nicola@p1" - defaultTokenAuthUser = "admin" - defaultTokenAuthPass = "password" - altAdminUsername = "newTestAdmin" - altAdminPassword = "password1" - csrfFormToken = "_form_token" - tokenPath = "/api/v2/token" - userTokenPath = "/api/v2/user/token" - userLogoutPath = "/api/v2/user/logout" - userPath = "/api/v2/users" - adminPath = "/api/v2/admins" - adminPwdPath = "/api/v2/admin/changepwd" - folderPath = "/api/v2/folders" - groupPath = "/api/v2/groups" - activeConnectionsPath = "/api/v2/connections" - serverStatusPath = "/api/v2/status" - quotasBasePath = "/api/v2/quotas" - quotaScanPath = "/api/v2/quotas/users/scans" - quotaScanVFolderPath = "/api/v2/quotas/folders/scans" - quotaScanCompatPath = "/api/v2/quota-scans" - quotaScanVFolderCompatPath = "/api/v2/folder-quota-scans" - updateUsedQuotaCompatPath = "/api/v2/quota-update" - updateFolderUsedQuotaCompatPath = "/api/v2/folder-quota-update" - defenderHosts = "/api/v2/defender/hosts" - defenderBanTime = "/api/v2/defender/bantime" - defenderUnban = "/api/v2/defender/unban" - defenderScore = "/api/v2/defender/score" - versionPath = "/api/v2/version" - logoutPath = "/api/v2/logout" - userPwdPath = "/api/v2/user/changepwd" - userPublicKeysPath = "/api/v2/user/publickeys" - userDirsPath = "/api/v2/user/dirs" - userFilesPath = "/api/v2/user/files" - userStreamZipPath = "/api/v2/user/streamzip" - userUploadFilePath = "/api/v2/user/files/upload" - userFilesDirsMetadataPath = "/api/v2/user/files/metadata" - apiKeysPath = "/api/v2/apikeys" - adminTOTPConfigsPath = "/api/v2/admin/totp/configs" - adminTOTPGeneratePath = "/api/v2/admin/totp/generate" - adminTOTPValidatePath = "/api/v2/admin/totp/validate" - adminTOTPSavePath = "/api/v2/admin/totp/save" - admin2FARecoveryCodesPath = "/api/v2/admin/2fa/recoverycodes" - adminProfilePath = "/api/v2/admin/profile" - userTOTPConfigsPath = "/api/v2/user/totp/configs" - userTOTPGeneratePath = "/api/v2/user/totp/generate" - userTOTPValidatePath = "/api/v2/user/totp/validate" - userTOTPSavePath = "/api/v2/user/totp/save" - user2FARecoveryCodesPath = "/api/v2/user/2fa/recoverycodes" - userProfilePath = "/api/v2/user/profile" - userSharesPath = "/api/v2/user/shares" - retentionBasePath = "/api/v2/retention/users" - metadataBasePath = "/api/v2/metadata/users" - fsEventsPath = "/api/v2/events/fs" - providerEventsPath = "/api/v2/events/provider" - sharesPath = "/api/v2/shares" - healthzPath = "/healthz" - robotsTxtPath = "/robots.txt" - webBasePath = "/web" - webBasePathAdmin = "/web/admin" - webAdminSetupPath = "/web/admin/setup" - webLoginPath = "/web/admin/login" - webLogoutPath = "/web/admin/logout" - webUsersPath = "/web/admin/users" - webUserPath = "/web/admin/user" - webGroupsPath = "/web/admin/groups" - webGroupPath = "/web/admin/group" - webFoldersPath = "/web/admin/folders" - webFolderPath = "/web/admin/folder" - webConnectionsPath = "/web/admin/connections" - webStatusPath = "/web/admin/status" - webAdminsPath = "/web/admin/managers" - webAdminPath = "/web/admin/manager" - webMaintenancePath = "/web/admin/maintenance" - webRestorePath = "/web/admin/restore" - webChangeAdminPwdPath = "/web/admin/changepwd" - webAdminProfilePath = "/web/admin/profile" - webTemplateUser = "/web/admin/template/user" - webTemplateFolder = "/web/admin/template/folder" - webDefenderPath = "/web/admin/defender" - webAdminTwoFactorPath = "/web/admin/twofactor" - webAdminTwoFactorRecoveryPath = "/web/admin/twofactor-recovery" - webAdminMFAPath = "/web/admin/mfa" - webAdminTOTPSavePath = "/web/admin/totp/save" - webAdminForgotPwdPath = "/web/admin/forgot-password" - webAdminResetPwdPath = "/web/admin/reset-password" - webBasePathClient = "/web/client" - webClientLoginPath = "/web/client/login" - webClientFilesPath = "/web/client/files" - webClientEditFilePath = "/web/client/editfile" - webClientDirsPath = "/web/client/dirs" - webClientDownloadZipPath = "/web/client/downloadzip" - webChangeClientPwdPath = "/web/client/changepwd" - webClientProfilePath = "/web/client/profile" - webClientTwoFactorPath = "/web/client/twofactor" - webClientTwoFactorRecoveryPath = "/web/client/twofactor-recovery" - webClientLogoutPath = "/web/client/logout" - webClientMFAPath = "/web/client/mfa" - webClientTOTPSavePath = "/web/client/totp/save" - webClientSharesPath = "/web/client/shares" - webClientSharePath = "/web/client/share" - webClientPubSharesPath = "/web/client/pubshares" - webClientForgotPwdPath = "/web/client/forgot-password" - webClientResetPwdPath = "/web/client/reset-password" - webClientViewPDFPath = "/web/client/viewpdf" - httpBaseURL = "http://127.0.0.1:8081" - defaultRemoteAddr = "127.0.0.1:1234" - sftpServerAddr = "127.0.0.1:8022" - smtpServerAddr = "127.0.0.1:3525" - configDir = ".." - httpsCert = `-----BEGIN CERTIFICATE----- + defaultUsername = "test_user" + defaultPassword = "test_password" + testPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1" + testPubKey1 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCd60+/j+y8f0tLftihWV1YN9RSahMI9btQMDIMqts/jeNbD8jgoogM3nhF7KxfcaMKURuD47KC4Ey6iAJUJ0sWkSNNxOcIYuvA+5MlspfZDsa8Ag76Fe1vyz72WeHMHMeh/hwFo2TeIeIXg480T1VI6mzfDrVp2GzUx0SS0dMsQBjftXkuVR8YOiOwMCAH2a//M1OrvV7d/NBk6kBN0WnuIBb2jKm15PAA7+jQQG7tzwk2HedNH3jeL5GH31xkSRwlBczRK0xsCQXehAlx6cT/e/s44iJcJTHfpPKoSk6UAhPJYe7Z1QnuoawY9P9jQaxpyeImBZxxUEowhjpj2avBxKdRGBVK8R7EL8tSOeLbhdyWe5Mwc1+foEbq9Zz5j5Kd+hn3Wm1UnsGCrXUUUoZp1jnlNl0NakCto+5KmqnT9cHxaY+ix2RLUWAZyVFlRq71OYux1UHJnEJPiEI1/tr4jFBSL46qhQZv/TfpkfVW8FLz0lErfqu0gQEZnNHr3Fc= nicola@p1" + defaultTokenAuthUser = "admin" + defaultTokenAuthPass = "password" + altAdminUsername = "newTestAdmin" + altAdminPassword = "password1" + csrfFormToken = "_form_token" + tokenPath = "/api/v2/token" + userTokenPath = "/api/v2/user/token" + userLogoutPath = "/api/v2/user/logout" + userPath = "/api/v2/users" + adminPath = "/api/v2/admins" + adminPwdPath = "/api/v2/admin/changepwd" + folderPath = "/api/v2/folders" + groupPath = "/api/v2/groups" + activeConnectionsPath = "/api/v2/connections" + serverStatusPath = "/api/v2/status" + quotasBasePath = "/api/v2/quotas" + quotaScanPath = "/api/v2/quotas/users/scans" + quotaScanVFolderPath = "/api/v2/quotas/folders/scans" + defenderHosts = "/api/v2/defender/hosts" + versionPath = "/api/v2/version" + logoutPath = "/api/v2/logout" + userPwdPath = "/api/v2/user/changepwd" + userDirsPath = "/api/v2/user/dirs" + userFilesPath = "/api/v2/user/files" + userStreamZipPath = "/api/v2/user/streamzip" + userUploadFilePath = "/api/v2/user/files/upload" + userFilesDirsMetadataPath = "/api/v2/user/files/metadata" + apiKeysPath = "/api/v2/apikeys" + adminTOTPConfigsPath = "/api/v2/admin/totp/configs" + adminTOTPGeneratePath = "/api/v2/admin/totp/generate" + adminTOTPValidatePath = "/api/v2/admin/totp/validate" + adminTOTPSavePath = "/api/v2/admin/totp/save" + admin2FARecoveryCodesPath = "/api/v2/admin/2fa/recoverycodes" + adminProfilePath = "/api/v2/admin/profile" + userTOTPConfigsPath = "/api/v2/user/totp/configs" + userTOTPGeneratePath = "/api/v2/user/totp/generate" + userTOTPValidatePath = "/api/v2/user/totp/validate" + userTOTPSavePath = "/api/v2/user/totp/save" + user2FARecoveryCodesPath = "/api/v2/user/2fa/recoverycodes" + userProfilePath = "/api/v2/user/profile" + userSharesPath = "/api/v2/user/shares" + retentionBasePath = "/api/v2/retention/users" + metadataBasePath = "/api/v2/metadata/users" + fsEventsPath = "/api/v2/events/fs" + providerEventsPath = "/api/v2/events/provider" + sharesPath = "/api/v2/shares" + healthzPath = "/healthz" + robotsTxtPath = "/robots.txt" + webBasePath = "/web" + webBasePathAdmin = "/web/admin" + webAdminSetupPath = "/web/admin/setup" + webLoginPath = "/web/admin/login" + webLogoutPath = "/web/admin/logout" + webUsersPath = "/web/admin/users" + webUserPath = "/web/admin/user" + webGroupsPath = "/web/admin/groups" + webGroupPath = "/web/admin/group" + webFoldersPath = "/web/admin/folders" + webFolderPath = "/web/admin/folder" + webConnectionsPath = "/web/admin/connections" + webStatusPath = "/web/admin/status" + webAdminsPath = "/web/admin/managers" + webAdminPath = "/web/admin/manager" + webMaintenancePath = "/web/admin/maintenance" + webRestorePath = "/web/admin/restore" + webChangeAdminPwdPath = "/web/admin/changepwd" + webAdminProfilePath = "/web/admin/profile" + webTemplateUser = "/web/admin/template/user" + webTemplateFolder = "/web/admin/template/folder" + webDefenderPath = "/web/admin/defender" + webAdminTwoFactorPath = "/web/admin/twofactor" + webAdminTwoFactorRecoveryPath = "/web/admin/twofactor-recovery" + webAdminMFAPath = "/web/admin/mfa" + webAdminTOTPSavePath = "/web/admin/totp/save" + webAdminForgotPwdPath = "/web/admin/forgot-password" + webAdminResetPwdPath = "/web/admin/reset-password" + webBasePathClient = "/web/client" + webClientLoginPath = "/web/client/login" + webClientFilesPath = "/web/client/files" + webClientEditFilePath = "/web/client/editfile" + webClientDirsPath = "/web/client/dirs" + webClientDownloadZipPath = "/web/client/downloadzip" + webChangeClientPwdPath = "/web/client/changepwd" + webClientProfilePath = "/web/client/profile" + webClientTwoFactorPath = "/web/client/twofactor" + webClientTwoFactorRecoveryPath = "/web/client/twofactor-recovery" + webClientLogoutPath = "/web/client/logout" + webClientMFAPath = "/web/client/mfa" + webClientTOTPSavePath = "/web/client/totp/save" + webClientSharesPath = "/web/client/shares" + webClientSharePath = "/web/client/share" + webClientPubSharesPath = "/web/client/pubshares" + webClientForgotPwdPath = "/web/client/forgot-password" + webClientResetPwdPath = "/web/client/reset-password" + webClientViewPDFPath = "/web/client/viewpdf" + httpBaseURL = "http://127.0.0.1:8081" + defaultRemoteAddr = "127.0.0.1:1234" + sftpServerAddr = "127.0.0.1:8022" + smtpServerAddr = "127.0.0.1:3525" + configDir = ".." + httpsCert = `-----BEGIN CERTIFICATE----- MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw @@ -1393,24 +1385,6 @@ func TestHTTPUserAuthentication(t *testing.T) { err = resp.Body.Close() assert.NoError(t, err) - req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userPublicKeysPath), nil) - assert.NoError(t, err) - req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken)) - resp, err = httpclient.GetHTTPClient().Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - err = resp.Body.Close() - assert.NoError(t, err) - // using the admin token should not work - req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userPublicKeysPath), nil) - assert.NoError(t, err) - req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", adminToken)) - resp, err = httpclient.GetHTTPClient().Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) - err = resp.Body.Close() - assert.NoError(t, err) - req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userLogoutPath), nil) assert.NoError(t, err) req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", adminToken)) @@ -1429,15 +1403,6 @@ func TestHTTPUserAuthentication(t *testing.T) { err = resp.Body.Close() assert.NoError(t, err) - req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userPublicKeysPath), nil) - assert.NoError(t, err) - req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken)) - resp, err = httpclient.GetHTTPClient().Do(req) - assert.NoError(t, err) - assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) - err = resp.Body.Close() - assert.NoError(t, err) - _, err = httpdtest.RemoveUser(user, http.StatusOK) assert.NoError(t, err) err = os.RemoveAll(user.GetHomeDir()) @@ -5191,35 +5156,14 @@ func TestDefenderAPI(t *testing.T) { ip := "::1" - response, _, err := httpdtest.GetBanTime(ip, http.StatusOK) - assert.NoError(t, err) - banTime, ok := response["date_time"] - assert.True(t, ok) - assert.Nil(t, banTime) - hosts, _, err := httpdtest.GetDefenderHosts(http.StatusOK) assert.NoError(t, err) assert.Len(t, hosts, 0) - response, _, err = httpdtest.GetScore(ip, http.StatusOK) - assert.NoError(t, err) - score, ok := response["score"] - assert.True(t, ok) - assert.Equal(t, float64(0), score) - - err = httpdtest.UnbanIP(ip, http.StatusNotFound) - assert.NoError(t, err) - _, err = httpdtest.RemoveDefenderHostByIP(ip, http.StatusNotFound) assert.NoError(t, err) common.AddDefenderEvent(ip, common.HostEventNoLoginTried) - response, _, err = httpdtest.GetScore(ip, http.StatusOK) - assert.NoError(t, err) - score, ok = response["score"] - assert.True(t, ok) - assert.Equal(t, float64(2), score) - hosts, _, err = httpdtest.GetDefenderHosts(http.StatusOK) assert.NoError(t, err) if assert.Len(t, hosts, 1) { @@ -5234,11 +5178,6 @@ func TestDefenderAPI(t *testing.T) { assert.Equal(t, 2, host.Score) common.AddDefenderEvent(ip, common.HostEventNoLoginTried) - response, _, err = httpdtest.GetBanTime(ip, http.StatusOK) - assert.NoError(t, err) - banTime, ok = response["date_time"] - assert.True(t, ok) - assert.NotNil(t, banTime) hosts, _, err = httpdtest.GetDefenderHosts(http.StatusOK) assert.NoError(t, err) if assert.Len(t, hosts, 1) { @@ -5252,13 +5191,10 @@ func TestDefenderAPI(t *testing.T) { assert.NotEmpty(t, host.GetBanTime()) assert.Equal(t, 0, host.Score) - err = httpdtest.UnbanIP(ip, http.StatusOK) + _, err = httpdtest.RemoveDefenderHostByIP(ip, http.StatusOK) assert.NoError(t, err) - err = httpdtest.UnbanIP(ip, http.StatusNotFound) - assert.NoError(t, err) - - host, _, err = httpdtest.GetDefenderHostByIP(ip, http.StatusNotFound) + _, _, err = httpdtest.GetDefenderHostByIP(ip, http.StatusNotFound) assert.NoError(t, err) common.AddDefenderEvent(ip, common.HostEventNoLoginTried) @@ -5290,17 +5226,6 @@ func TestDefenderAPI(t *testing.T) { } func TestDefenderAPIErrors(t *testing.T) { - _, _, err := httpdtest.GetBanTime("", http.StatusBadRequest) - require.NoError(t, err) - - _, _, err = httpdtest.GetBanTime("invalid", http.StatusBadRequest) - require.NoError(t, err) - - _, _, err = httpdtest.GetScore("", http.StatusBadRequest) - require.NoError(t, err) - - err = httpdtest.UnbanIP("", http.StatusBadRequest) - require.NoError(t, err) if isDbDefenderSupported() { oldConfig := config.GetCommonConfig() @@ -5316,26 +5241,12 @@ func TestDefenderAPIErrors(t *testing.T) { err = dataprovider.Close() assert.NoError(t, err) - ip := "127.1.1.2" - req, err := http.NewRequest(http.MethodGet, defenderHosts, nil) assert.NoError(t, err) setBearerForReq(req, token) rr := executeRequest(req) checkResponseCode(t, http.StatusInternalServerError, rr) - req, err = http.NewRequest(http.MethodGet, defenderBanTime+"?ip="+ip, nil) - assert.NoError(t, err) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusInternalServerError, rr) - - req, err = http.NewRequest(http.MethodGet, defenderScore+"?ip="+ip, nil) - assert.NoError(t, err) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusInternalServerError, rr) - err = config.LoadConfig(configDir, "") assert.NoError(t, err) providerConf := config.GetProviderConf() @@ -5999,15 +5910,6 @@ func TestUpdateFolderInvalidJsonMock(t *testing.T) { assert.NoError(t, err) } -func TestUnbanInvalidJsonMock(t *testing.T) { - token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) - assert.NoError(t, err) - req, _ := http.NewRequest(http.MethodPost, defenderUnban, bytes.NewBuffer([]byte("invalid json"))) - setBearerForReq(req, token) - rr := executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) -} - func TestAddUserInvalidJsonMock(t *testing.T) { token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) assert.NoError(t, err) @@ -8343,10 +8245,6 @@ func TestUpdateUserQuotaUsageMock(t *testing.T) { setBearerForReq(req, token) rr = executeRequest(req) checkResponseCode(t, http.StatusOK, rr) - req, _ = http.NewRequest(http.MethodPut, updateUsedQuotaCompatPath, bytes.NewBuffer(userAsJSON)) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil) setBearerForReq(req, token) rr = executeRequest(req) @@ -8386,10 +8284,6 @@ func TestUpdateUserQuotaUsageMock(t *testing.T) { assert.NoError(t, err) assert.Equal(t, usedQuotaFiles*2, user.UsedQuotaFiles) assert.Equal(t, usedQuotaSize*2, user.UsedQuotaSize) - req, _ = http.NewRequest(http.MethodPut, updateUsedQuotaCompatPath, bytes.NewBuffer([]byte("string"))) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "users", u.Username, "usage"), bytes.NewBuffer([]byte("string"))) setBearerForReq(req, token) rr = executeRequest(req) @@ -8704,15 +8598,6 @@ func TestStartQuotaScanMock(t *testing.T) { waitForUsersQuotaScan(t, token) - asJSON, err := json.Marshal(user) - assert.NoError(t, err) - req, _ = http.NewRequest(http.MethodPost, quotaScanCompatPath, bytes.NewBuffer(asJSON)) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusAccepted, rr) - - waitForUsersQuotaScan(t, token) - req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil) setBearerForReq(req, token) rr = executeRequest(req) @@ -8747,10 +8632,6 @@ func TestUpdateFolderQuotaUsageMock(t *testing.T) { setBearerForReq(req, token) rr = executeRequest(req) checkResponseCode(t, http.StatusOK, rr) - req, _ = http.NewRequest(http.MethodPut, updateFolderUsedQuotaCompatPath, bytes.NewBuffer(folderAsJSON)) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) var folderGet vfs.BaseVirtualFolder req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil) setBearerForReq(req, token) @@ -8797,10 +8678,6 @@ func TestUpdateFolderQuotaUsageMock(t *testing.T) { assert.NoError(t, err) assert.Equal(t, usedQuotaFiles*2, folderGet.UsedQuotaFiles) assert.Equal(t, usedQuotaSize*2, folderGet.UsedQuotaSize) - req, _ = http.NewRequest(http.MethodPut, updateFolderUsedQuotaCompatPath, bytes.NewBuffer([]byte("string"))) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "folders", folder.Name, "usage"), bytes.NewBuffer([]byte("not a json"))) setBearerForReq(req, token) @@ -8859,17 +8736,7 @@ func TestStartFolderQuotaScanMock(t *testing.T) { rr = executeRequest(req) checkResponseCode(t, http.StatusAccepted, rr) waitForFoldersQuotaScanPath(t, token) - - asJSON, err := json.Marshal(folder) - assert.NoError(t, err) - req, _ = http.NewRequest(http.MethodPost, quotaScanVFolderCompatPath, bytes.NewBuffer(asJSON)) - setBearerForReq(req, token) - rr = executeRequest(req) - checkResponseCode(t, http.StatusAccepted, rr) - waitForFoldersQuotaScanPath(t, token) - // cleanup - req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil) setBearerForReq(req, token) rr = executeRequest(req) @@ -8891,24 +8758,6 @@ func TestStartQuotaScanNonExistentUserMock(t *testing.T) { checkResponseCode(t, http.StatusNotFound, rr) } -func TestStartQuotaScanBadUserMock(t *testing.T) { - token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) - assert.NoError(t, err) - req, _ := http.NewRequest(http.MethodPost, quotaScanCompatPath, bytes.NewBuffer([]byte("invalid json"))) - setBearerForReq(req, token) - rr := executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) -} - -func TestStartQuotaScanBadFolderMock(t *testing.T) { - token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) - assert.NoError(t, err) - req, _ := http.NewRequest(http.MethodPost, quotaScanVFolderCompatPath, bytes.NewBuffer([]byte("invalid json"))) - setBearerForReq(req, token) - rr := executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) -} - func TestStartQuotaScanNonExistentFolderMock(t *testing.T) { token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass) assert.NoError(t, err) @@ -9327,18 +9176,6 @@ func TestWebClientLoginMock(t *testing.T) { checkResponseCode(t, http.StatusNotFound, rr) assert.Contains(t, rr.Body.String(), "Unable to retrieve your user") - req, _ = http.NewRequest(http.MethodGet, userPublicKeysPath, nil) - setBearerForReq(req, apiUserToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusNotFound, rr) - assert.Contains(t, rr.Body.String(), "Unable to retrieve your user") - - req, _ = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer([]byte(`{}`))) - setBearerForReq(req, apiUserToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusNotFound, rr) - assert.Contains(t, rr.Body.String(), "Unable to retrieve your user") - csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath) assert.NoError(t, err) form := make(url.Values) @@ -9872,75 +9709,6 @@ func TestWebClientChangePwd(t *testing.T) { assert.NoError(t, err) } -func TestWebAPIPublicKeys(t *testing.T) { - user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) - assert.NoError(t, err) - apiToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword) - assert.NoError(t, err) - - req, err := http.NewRequest(http.MethodGet, userPublicKeysPath, nil) - assert.NoError(t, err) - setBearerForReq(req, apiToken) - rr := executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) - var keys []string - err = json.Unmarshal(rr.Body.Bytes(), &keys) - assert.NoError(t, err) - assert.Len(t, keys, 0) - - keys = []string{testPubKey, testPubKey1} - asJSON, err := json.Marshal(keys) - assert.NoError(t, err) - req, err = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer(asJSON)) - assert.NoError(t, err) - setBearerForReq(req, apiToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) - - req, err = http.NewRequest(http.MethodGet, userPublicKeysPath, nil) - assert.NoError(t, err) - setBearerForReq(req, apiToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusOK, rr) - keys = nil - err = json.Unmarshal(rr.Body.Bytes(), &keys) - assert.NoError(t, err) - assert.Len(t, keys, 2) - - req, err = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer([]byte(`invalid json`))) - assert.NoError(t, err) - setBearerForReq(req, apiToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) - - keys = []string{`not a public key`} - asJSON, err = json.Marshal(keys) - assert.NoError(t, err) - req, err = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer(asJSON)) - assert.NoError(t, err) - setBearerForReq(req, apiToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusBadRequest, rr) - assert.Contains(t, rr.Body.String(), "could not parse key") - - user.Filters.WebClient = append(user.Filters.WebClient, sdk.WebClientPubKeyChangeDisabled) - _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") - assert.NoError(t, err) - - apiToken, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword) - assert.NoError(t, err) - req, err = http.NewRequest(http.MethodGet, userPublicKeysPath, nil) - assert.NoError(t, err) - setBearerForReq(req, apiToken) - rr = executeRequest(req) - checkResponseCode(t, http.StatusForbidden, rr) - - _, err = httpdtest.RemoveUser(user, http.StatusOK) - assert.NoError(t, err) - err = os.RemoveAll(user.GetHomeDir()) - assert.NoError(t, err) -} - func TestPreDownloadHook(t *testing.T) { if runtime.GOOS == osWindows { t.Skip("this test is not available on Windows") diff --git a/httpd/internal_test.go b/httpd/internal_test.go index 2fb63fd6..09d45c3b 100644 --- a/httpd/internal_test.go +++ b/httpd/internal_test.go @@ -461,16 +461,6 @@ func TestInvalidToken(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rr.Code) assert.Contains(t, rr.Body.String(), "Invalid token claims") - rr = httptest.NewRecorder() - getUserPublicKeys(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid token claims") - - rr = httptest.NewRecorder() - setUserPublicKeys(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid token claims") - rr = httptest.NewRecorder() generateTOTPSecret(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) @@ -1079,8 +1069,8 @@ func TestJWTTokenValidation(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rr.Code) rr = httptest.NewRecorder() - req, _ = http.NewRequest(http.MethodPost, userPublicKeysPath, nil) - req.RequestURI = userPublicKeysPath + req, _ = http.NewRequest(http.MethodPost, userProfilePath, nil) + req.RequestURI = userProfilePath ctx = jwtauth.NewContext(req.Context(), token, errTest) fn.ServeHTTP(rr, req.WithContext(ctx)) assert.Equal(t, http.StatusBadRequest, rr.Code) diff --git a/httpd/server.go b/httpd/server.go index aae8c4db..328c42fb 100644 --- a/httpd/server.go +++ b/httpd/server.go @@ -1179,8 +1179,6 @@ func (s *httpdServer) initializeRouter() { router.With(forbidAPIKeyAuthentication).Get(adminProfilePath, getAdminProfile) router.With(forbidAPIKeyAuthentication).Put(adminProfilePath, updateAdminProfile) router.With(forbidAPIKeyAuthentication).Put(adminPwdPath, changeAdminPassword) - // compatibility layer to remove in v2.2 - router.With(forbidAPIKeyAuthentication).Put(adminPwdCompatPath, changeAdminPassword) // admin TOTP APIs router.With(forbidAPIKeyAuthentication).Get(adminTOTPConfigsPath, getTOTPConfigs) router.With(forbidAPIKeyAuthentication).Post(adminTOTPGeneratePath, generateTOTPSecret) @@ -1203,13 +1201,9 @@ func (s *httpdServer) initializeRouter() { router.With(s.checkPerm(dataprovider.PermAdminCloseConnections)). Delete(activeConnectionsPath+"/{connectionID}", handleCloseConnection) - router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotaScanPath, getUsersQuotaScans) router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/users/scans", getUsersQuotaScans) - router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotaScanPath, startUserQuotaScanCompat) router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/users/{username}/scan", startUserQuotaScan) - router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotaScanVFolderPath, getFoldersQuotaScans) router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/folders/scans", getFoldersQuotaScans) - router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotaScanVFolderPath, startFolderQuotaScanCompat) router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/folders/{name}/scan", startFolderQuotaScan) router.With(s.checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath, getUsers) router.With(s.checkPerm(dataprovider.PermAdminAddUsers)).Post(userPath, addUser) @@ -1230,20 +1224,15 @@ func (s *httpdServer) initializeRouter() { router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(dumpDataPath, dumpData) router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(loadDataPath, loadData) router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(loadDataPath, loadDataFromRequest) - router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(updateUsedQuotaPath, updateUserQuotaUsageCompat) router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/usage", updateUserQuotaUsage) router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/transfer-usage", updateUserTransferQuotaUsage) - router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(updateFolderUsedQuotaPath, updateFolderQuotaUsageCompat) router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/folders/{name}/usage", updateFolderQuotaUsage) router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts, getDefenderHosts) router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts+"/{id}", getDefenderHostByID) router.With(s.checkPerm(dataprovider.PermAdminManageDefender)).Delete(defenderHosts+"/{id}", deleteDefenderHostByID) - router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderBanTime, getBanTime) - router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderScore, getScore) - router.With(s.checkPerm(dataprovider.PermAdminManageDefender)).Post(defenderUnban, unban) router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath, getAdmins) router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(adminPath, addAdmin) router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath+"/{username}", getAdminByUsername) @@ -1282,10 +1271,6 @@ func (s *httpdServer) initializeRouter() { router.With(forbidAPIKeyAuthentication).Get(userLogoutPath, s.logout) router.With(forbidAPIKeyAuthentication, s.checkSecondFactorRequirement, s.checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).Put(userPwdPath, changeUserPassword) - router.With(forbidAPIKeyAuthentication, s.checkSecondFactorRequirement, - s.checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).Get(userPublicKeysPath, getUserPublicKeys) - router.With(forbidAPIKeyAuthentication, s.checkSecondFactorRequirement, - s.checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).Put(userPublicKeysPath, setUserPublicKeys) router.With(forbidAPIKeyAuthentication).Get(userProfilePath, getUserProfile) router.With(forbidAPIKeyAuthentication, s.checkSecondFactorRequirement).Put(userProfilePath, updateUserProfile) // user TOTP APIs @@ -1302,10 +1287,6 @@ func (s *httpdServer) initializeRouter() { router.With(forbidAPIKeyAuthentication, s.checkHTTPUserPerm(sdk.WebClientMFADisabled)). Post(user2FARecoveryCodesPath, generateRecoveryCodes) - // compatibility layer to remove in v2.3 - router.With(s.checkSecondFactorRequirement, compressor.Handler).Get(userFolderPath, readUserFolder) - router.With(s.checkSecondFactorRequirement).Get(userFilePath, getUserFile) - router.With(s.checkSecondFactorRequirement, compressor.Handler).Get(userDirsPath, readUserFolder) router.With(s.checkSecondFactorRequirement, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled)). Post(userDirsPath, createUserDir) diff --git a/httpdtest/httpdtest.go b/httpdtest/httpdtest.go index f5aaddb1..8b56ac2b 100644 --- a/httpdtest/httpdtest.go +++ b/httpdtest/httpdtest.go @@ -41,9 +41,6 @@ const ( dumpDataPath = "/api/v2/dumpdata" loadDataPath = "/api/v2/loaddata" defenderHosts = "/api/v2/defender/hosts" - defenderBanTime = "/api/v2/defender/bantime" - defenderUnban = "/api/v2/defender/unban" - defenderScore = "/api/v2/defender/score" adminPath = "/api/v2/admins" adminPwdPath = "/api/v2/admin/changepwd" apiKeysPath = "/api/v2/apikeys" @@ -984,70 +981,6 @@ func RemoveDefenderHostByIP(ip string, expectedStatusCode int) ([]byte, error) { return body, checkResponse(resp.StatusCode, expectedStatusCode) } -// GetBanTime returns the ban time for the given IP address -func GetBanTime(ip string, expectedStatusCode int) (map[string]any, []byte, error) { - var response map[string]any - var body []byte - url, err := url.Parse(buildURLRelativeToBase(defenderBanTime)) - if err != nil { - return response, body, err - } - q := url.Query() - q.Add("ip", ip) - url.RawQuery = q.Encode() - resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken()) - if err != nil { - return response, body, err - } - defer resp.Body.Close() - err = checkResponse(resp.StatusCode, expectedStatusCode) - if err == nil && expectedStatusCode == http.StatusOK { - err = render.DecodeJSON(resp.Body, &response) - } else { - body, _ = getResponseBody(resp) - } - return response, body, err -} - -// GetScore returns the score for the given IP address -func GetScore(ip string, expectedStatusCode int) (map[string]any, []byte, error) { - var response map[string]any - var body []byte - url, err := url.Parse(buildURLRelativeToBase(defenderScore)) - if err != nil { - return response, body, err - } - q := url.Query() - q.Add("ip", ip) - url.RawQuery = q.Encode() - resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken()) - if err != nil { - return response, body, err - } - defer resp.Body.Close() - err = checkResponse(resp.StatusCode, expectedStatusCode) - if err == nil && expectedStatusCode == http.StatusOK { - err = render.DecodeJSON(resp.Body, &response) - } else { - body, _ = getResponseBody(resp) - } - return response, body, err -} - -// UnbanIP unbans the given IP address -func UnbanIP(ip string, expectedStatusCode int) error { - postBody := make(map[string]string) - postBody["ip"] = ip - asJSON, _ := json.Marshal(postBody) - resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(defenderUnban), bytes.NewBuffer(asJSON), - "", getDefaultToken()) - if err != nil { - return err - } - defer resp.Body.Close() - return checkResponse(resp.StatusCode, expectedStatusCode) -} - // Dumpdata requests a backup to outputFile. // outputFile is relative to the configured backups_path func Dumpdata(outputFile, outputData, indent string, expectedStatusCode int) (map[string]any, []byte, error) { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 48caac67..5ac504f3 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -439,37 +439,6 @@ paths: $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' - /changepwd/admin: - put: - security: - - BearerAuth: [] - tags: - - admins - summary: Change admin password - description: Changes the password for the logged in admin. Please use '/admin/changepwd' instead - operationId: change_admin_password_deprecated - deprecated: true - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PwdChange' - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' /admin/changepwd: put: security: @@ -896,107 +865,6 @@ paths: $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' - /defender/bantime: - get: - deprecated: true - tags: - - defender - summary: Get ban time - description: Deprecated, please use '/defender/hosts', '/defender/hosts/{id}' instead - operationId: get_ban_time - parameters: - - in: query - name: ip - required: true - description: IPv4/IPv6 address - schema: - type: string - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/BanStatus' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - /defender/unban: - post: - deprecated: true - tags: - - defender - summary: Unban - description: Deprecated, please use '/defender/hosts/{id}' instead - operationId: unban_host - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - ip: - type: string - description: IPv4/IPv6 address to remove - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - /defender/score: - get: - deprecated: true - tags: - - defender - summary: Get score - description: Deprecated, please use '/defender/hosts', '/defender/hosts/{id}' instead - operationId: get_score - parameters: - - in: query - name: ip - required: true - description: IPv4/IPv6 address - schema: - type: string - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ScoreStatus' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' /metadata/users/checks: get: tags: @@ -1447,230 +1315,6 @@ paths: $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' - /quota-scans: - get: - deprecated: true - tags: - - quota - summary: Get quota scans - description: Deprecated, please use '/quotas/users/scans' instead - operationId: get_users_quota_scans_deprecated - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/QuotaScan' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - post: - deprecated: true - tags: - - quota - summary: Start user quota scan - description: Deprecated, please use '/quotas/users/{username}/scan' instead - operationId: start_user_quota_scan_deprecated - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/User' - responses: - '202': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - example: - message: Scan started - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '409': - $ref: '#/components/responses/Conflict' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - /quota-update: - put: - deprecated: true - tags: - - quota - summary: Update quota usage limits - description: Deprecated, please use '/quotas/users/{username}/usage' instead - operationId: user_quota_update_usage_deprecated - parameters: - - in: query - name: mode - required: false - description: the update mode specifies if the given quota usage values should be added or replace the current ones - schema: - type: string - enum: - - add - - reset - description: | - Update type: - * `add` - add the specified quota limits to the current used ones - * `reset` - reset the values to the specified ones. This is the default - example: reset - requestBody: - required: true - description: 'The only user mandatory fields are username, used_quota_size and used_quota_files. Please note that if the quota fields are missing they will default to 0, this means that if mode is "add" the current value will remain unchanged, if mode is "reset" the missing field is set to 0' - content: - application/json: - schema: - $ref: '#/components/schemas/User' - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - example: - message: Quota updated - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - /folder-quota-update: - put: - deprecated: true - tags: - - quota - summary: Update folder quota limits - description: Deprecated, please use '/quotas/folders/{name}/usage' instead - operationId: folder_quota_update_usage_deprecated - parameters: - - in: query - name: mode - required: false - description: the update mode specifies if the given quota usage values should be added or replace the current ones - schema: - type: string - enum: - - add - - reset - description: | - Update type: - * `add` - add the specified quota limits to the current used ones - * `reset` - reset the values to the specified ones. This is the default - example: reset - requestBody: - required: true - description: 'The only folder mandatory fields are mapped_path,used_quota_size and used_quota_files. Please note that if the used quota fields are missing they will default to 0, this means that if mode is "add" the current value will remain unchanged, if mode is "reset" the missing field is set to 0' - content: - application/json: - schema: - $ref: '#/components/schemas/BaseVirtualFolder' - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - example: - message: Quota updated - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - /folder-quota-scans: - get: - deprecated: true - tags: - - quota - summary: Get folders quota scans - description: Deprecated, please use '/quotas/folders/scans' instead - operationId: get_folders_quota_scans_deprecated - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/FolderQuotaScan' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - post: - deprecated: true - tags: - - quota - summary: Start a folder quota scan - description: Deprecated, please use '/quotas/folders/{name}/scan' instead - operationId: start_folder_quota_scan_deprecated - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BaseVirtualFolder' - responses: - '202': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - example: - message: Scan started - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '409': - $ref: '#/components/responses/Conflict' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' /folders: get: tags: @@ -3291,71 +2935,6 @@ paths: $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' - /user/publickeys: - get: - security: - - BearerAuth: [] - tags: - - user APIs - deprecated: true - summary: Get the user's public keys - description: 'Returns the public keys for the logged in user. Deprecated please use "/user/profile" instead' - operationId: get_user_public_keys - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: array - items: - type: string - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' - put: - security: - - BearerAuth: [] - tags: - - user APIs - deprecated: true - summary: Set the user's public keys - description: 'Sets the public keys for the logged in user. Public keys must be in OpenSSH format. Deprecated please use "/user/profile" instead' - operationId: set_user_public_keys - requestBody: - required: true - content: - application/json: - schema: - type: array - items: - type: string - description: Public key in OpenSSH format - example: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPVILdH2u3yV5SAeE6XksD1z1vXRg0E4hJUov8ITDAZ2 user@host - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' /user/profile: get: security: @@ -3799,39 +3378,6 @@ paths: $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' - /user/folder: - get: - tags: - - user APIs - summary: Read folders contents - description: Returns the contents of the specified folder for the logged in user. Please use '/user/dirs' instead - operationId: get_user_folder_contents - deprecated: true - parameters: - - in: query - name: path - description: Path to the folder to read. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir". If empty or missing the user's start directory is assumed. If relative, the user's start directory is used as the base - schema: - type: string - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/DirEntry' - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' /user/dirs: get: tags: @@ -3972,46 +3518,6 @@ paths: $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' - /user/file: - get: - tags: - - user APIs - summary: Download a single file - description: Returns the file contents as response body. Please use '/user/files' instead - operationId: get_user_file - deprecated: true - parameters: - - in: query - name: path - required: true - description: Path to the file to download. It must be URL encoded, for example the path "my dir/àdir/file.txt" must be sent as "my%20dir%2F%C3%A0dir%2Ffile.txt" - schema: - type: string - responses: - '200': - description: successful operation - content: - '*/*': - schema: - type: string - format: binary - '206': - description: successful operation - content: - '*/*': - schema: - type: string - format: binary - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - default: - $ref: '#/components/responses/DefaultResponse' /user/files: get: tags: