Browse Source

remove deprecated APIs

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Nicola Murino 3 years ago
parent
commit
686166f2ce
13 changed files with 117 additions and 1132 deletions
  1. 1 1
      README.md
  2. 1 1
      README.zh_CN.md
  3. 2 2
      go.mod
  4. 4 4
      go.sum
  5. 0 84
      httpd/api_defender.go
  6. 0 45
      httpd/api_http_user.go
  7. 0 53
      httpd/api_quota.go
  8. 0 11
      httpd/httpd.go
  9. 107 339
      httpd/httpd_test.go
  10. 2 12
      httpd/internal_test.go
  11. 0 19
      httpd/server.go
  12. 0 67
      httpdtest/httpdtest.go
  13. 0 494
      openapi/openapi.yaml

+ 1 - 1
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)

+ 1 - 1
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)

+ 2 - 2
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

+ 4 - 4
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=

+ 0 - 84
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)
 	}

+ 0 - 45
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)

+ 0 - 53
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

+ 0 - 11
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"

+ 107 - 339
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")

+ 2 - 12
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)

+ 0 - 19
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)

+ 0 - 67
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) {

+ 0 - 494
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: