remove deprecated APIs

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-06-14 18:30:57 +02:00
parent 93ce593ed0
commit 686166f2ce
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
13 changed files with 117 additions and 1132 deletions

View file

@ -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)

View file

@ -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)

4
go.mod
View file

@ -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

8
go.sum
View file

@ -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=

View file

@ -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)
}

View file

@ -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)

View file

@ -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

View file

@ -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"

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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) {

View file

@ -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: