Selaa lähdekoodia

api_utils: return response body too

useful for debug and to build external API, for example a CLI interface
Nicola Murino 6 vuotta sitten
vanhempi
commit
4dc52ee3e9
4 muutettua tiedostoa jossa 225 lisäystä ja 176 poistoa
  1. 70 46
      api/api_test.go
  2. 74 53
      api/api_utils.go
  3. 18 14
      api/internal_test.go
  4. 63 63
      sftpd/sftpd_test.go

+ 70 - 46
api/api_test.go

@@ -97,7 +97,7 @@ func TestMain(m *testing.M) {
 }
 
 func TestBasicUserHandling(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -106,18 +106,18 @@ func TestBasicUserHandling(t *testing.T) {
 	user.QuotaFiles = 2
 	user.UploadBandwidth = 128
 	user.DownloadBandwidth = 64
-	user, err = api.UpdateUser(user, http.StatusOK)
+	user, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
-	users, err := api.GetUsers(0, 0, defaultUsername, http.StatusOK)
+	users, _, err := api.GetUsers(0, 0, defaultUsername, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 	}
 	if len(users) != 1 {
 		t.Errorf("number of users mismatch, expected: 1, actual: %v", len(users))
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 	}
@@ -127,7 +127,7 @@ func TestAddUserNoCredentials(t *testing.T) {
 	u := getTestUser()
 	u.Password = ""
 	u.PublicKey = []string{}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with no credentials: %v", err)
 	}
@@ -136,7 +136,7 @@ func TestAddUserNoCredentials(t *testing.T) {
 func TestAddUserNoUsername(t *testing.T) {
 	u := getTestUser()
 	u.Username = ""
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with no home dir: %v", err)
 	}
@@ -145,7 +145,7 @@ func TestAddUserNoUsername(t *testing.T) {
 func TestAddUserNoHomeDir(t *testing.T) {
 	u := getTestUser()
 	u.HomeDir = ""
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with no home dir: %v", err)
 	}
@@ -154,7 +154,7 @@ func TestAddUserNoHomeDir(t *testing.T) {
 func TestAddUserInvalidHomeDir(t *testing.T) {
 	u := getTestUser()
 	u.HomeDir = "relative_path"
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with invalid home dir: %v", err)
 	}
@@ -163,7 +163,7 @@ func TestAddUserInvalidHomeDir(t *testing.T) {
 func TestAddUserNoPerms(t *testing.T) {
 	u := getTestUser()
 	u.Permissions = []string{}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with no perms: %v", err)
 	}
@@ -172,7 +172,7 @@ func TestAddUserNoPerms(t *testing.T) {
 func TestAddUserInvalidPerms(t *testing.T) {
 	u := getTestUser()
 	u.Permissions = []string{"invalidPerm"}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with no perms: %v", err)
 	}
@@ -183,33 +183,33 @@ func TestUserPublicKey(t *testing.T) {
 	invalidPubKey := "invalid"
 	validPubKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
 	u.PublicKey = []string{invalidPubKey}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error adding user with invalid pub key: %v", err)
 	}
 	u.PublicKey = []string{validPubKey}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
 	user.PublicKey = []string{validPubKey, invalidPubKey}
-	_, err = api.UpdateUser(user, http.StatusBadRequest)
+	_, _, err = api.UpdateUser(user, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("update user with invalid public key must fail: %v", err)
 	}
 	user.PublicKey = []string{validPubKey, validPubKey, validPubKey}
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 	}
 }
 
 func TestUpdateUser(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -222,18 +222,18 @@ func TestUpdateUser(t *testing.T) {
 	user.Permissions = []string{dataprovider.PermCreateDirs, dataprovider.PermDelete, dataprovider.PermDownload}
 	user.UploadBandwidth = 1024
 	user.DownloadBandwidth = 512
-	user, err = api.UpdateUser(user, http.StatusOK)
+	user, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 	}
 }
 
 func TestUpdateUserNoCredentials(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -241,157 +241,173 @@ func TestUpdateUserNoCredentials(t *testing.T) {
 	user.PublicKey = []string{}
 	// password and public key will be omitted from json serialization if empty and so they will remain unchanged
 	// and no validation error will be raised
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unexpected error updating user with no credentials: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 	}
 }
 
 func TestUpdateUserEmptyHomeDir(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
 	user.HomeDir = ""
-	_, err = api.UpdateUser(user, http.StatusBadRequest)
+	_, _, err = api.UpdateUser(user, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error updating user with empty home dir: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 	}
 }
 
 func TestUpdateUserInvalidHomeDir(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
 	user.HomeDir = "relative_path"
-	_, err = api.UpdateUser(user, http.StatusBadRequest)
+	_, _, err = api.UpdateUser(user, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("unexpected error updating user with empty home dir: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 	}
 }
 
 func TestUpdateNonExistentUser(t *testing.T) {
-	_, err := api.UpdateUser(getTestUser(), http.StatusNotFound)
+	_, _, err := api.UpdateUser(getTestUser(), http.StatusNotFound)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
 }
 
 func TestGetNonExistentUser(t *testing.T) {
-	_, err := api.GetUserByID(0, http.StatusNotFound)
+	_, _, err := api.GetUserByID(0, http.StatusNotFound)
 	if err != nil {
 		t.Errorf("unable to get user: %v", err)
 	}
 }
 
 func TestDeleteNonExistentUser(t *testing.T) {
-	err := api.RemoveUser(getTestUser(), http.StatusNotFound)
+	_, err := api.RemoveUser(getTestUser(), http.StatusNotFound)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
 }
 
 func TestAddDuplicateUser(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
-	_, err = api.AddUser(getTestUser(), http.StatusInternalServerError)
+	_, _, err = api.AddUser(getTestUser(), http.StatusInternalServerError)
 	if err != nil {
 		t.Errorf("unable to add second user: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, _, err = api.AddUser(getTestUser(), http.StatusOK)
+	if err == nil {
+		t.Errorf("adding a duplicate user must fail")
+	}
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
 }
 
 func TestGetUsers(t *testing.T) {
-	user1, err := api.AddUser(getTestUser(), http.StatusOK)
+	user1, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
 	u := getTestUser()
 	u.Username = defaultUsername + "1"
-	user2, err := api.AddUser(u, http.StatusOK)
+	user2, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add second user: %v", err)
 	}
-	users, err := api.GetUsers(0, 0, "", http.StatusOK)
+	users, _, err := api.GetUsers(0, 0, "", http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 	}
 	if len(users) < 2 {
 		t.Errorf("at least 2 users are expected")
 	}
-	users, err = api.GetUsers(1, 0, "", http.StatusOK)
+	users, _, err = api.GetUsers(1, 0, "", http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 	}
 	if len(users) != 1 {
 		t.Errorf("1 user is expected")
 	}
-	users, err = api.GetUsers(1, 1, "", http.StatusOK)
+	users, _, err = api.GetUsers(1, 1, "", http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 	}
 	if len(users) != 1 {
 		t.Errorf("1 user is expected")
 	}
-	err = api.RemoveUser(user1, http.StatusOK)
+	_, _, err = api.GetUsers(1, 1, "", http.StatusInternalServerError)
+	if err == nil {
+		t.Errorf("get users must succeed, we requested a fail for a good request")
+	}
+	_, err = api.RemoveUser(user1, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
-	err = api.RemoveUser(user2, http.StatusOK)
+	_, err = api.RemoveUser(user2, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
 }
 
 func TestGetQuotaScans(t *testing.T) {
-	_, err := api.GetQuotaScans(http.StatusOK)
+	_, _, err := api.GetQuotaScans(http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to get quota scans: %v", err)
 	}
+	_, _, err = api.GetQuotaScans(http.StatusInternalServerError)
+	if err == nil {
+		t.Errorf("quota scan request must succeed, we requested to check a wrong status code")
+	}
 }
 
 func TestStartQuotaScan(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
-	err = api.StartQuotaScan(user, http.StatusCreated)
+	_, err = api.StartQuotaScan(user, http.StatusCreated)
 	if err != nil {
 		t.Errorf("unable to start quota scan: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
 }
 
 func TestGetSFTPConnections(t *testing.T) {
-	_, err := api.GetSFTPConnections(http.StatusOK)
+	_, _, err := api.GetSFTPConnections(http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to get sftp connections: %v", err)
 	}
+	_, _, err = api.GetSFTPConnections(http.StatusInternalServerError)
+	if err == nil {
+		t.Errorf("get sftp connections request must succeed, we requested to check a wrong status code")
+	}
 }
 
 func TestCloseActiveSFTPConnection(t *testing.T) {
-	err := api.CloseSFTPConnection("non_existent_id", http.StatusNotFound)
+	_, err := api.CloseSFTPConnection("non_existent_id", http.StatusNotFound)
 	if err != nil {
 		t.Errorf("unexpected error closing non existent sftp connection: %v", err)
 	}
@@ -597,6 +613,14 @@ func TestStartQuotaScanMock(t *testing.T) {
 	if err == nil {
 		os.Remove(user.HomeDir)
 	}
+	// simulate a duplicate quota scan
+	userAsJSON = getUserAsJSON(t, user)
+	sftpd.AddQuotaScan(user.Username)
+	req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
+	rr = executeRequest(req)
+	checkResponseCode(t, http.StatusConflict, rr.Code)
+	sftpd.RemoveQuotaScan(user.Username)
+
 	userAsJSON = getUserAsJSON(t, user)
 	req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
 	rr = executeRequest(req)

+ 74 - 53
api/api_utils.go

@@ -12,15 +12,13 @@ import (
 	"time"
 
 	"github.com/drakkan/sftpgo/dataprovider"
-	"github.com/drakkan/sftpgo/logger"
 	"github.com/drakkan/sftpgo/sftpd"
 	"github.com/drakkan/sftpgo/utils"
 	"github.com/go-chi/render"
 )
 
 var (
-	defaultPerms = []string{dataprovider.PermAny}
-	httpBaseURL  = "http://127.0.0.1:8080"
+	httpBaseURL = "http://127.0.0.1:8080"
 )
 
 // SetBaseURL sets the base url to use for HTTP requests, default is "http://127.0.0.1:8080"
@@ -36,97 +34,109 @@ func getHTTPClient() *http.Client {
 }
 
 // AddUser adds a new user and checks the received HTTP Status code against expectedStatusCode.
-func AddUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User, error) {
+func AddUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User, []byte, error) {
 	var newUser dataprovider.User
+	var body []byte
 	userAsJSON, err := json.Marshal(user)
 	if err != nil {
-		return newUser, err
+		return newUser, body, err
 	}
 	resp, err := getHTTPClient().Post(httpBaseURL+userPath, "application/json", bytes.NewBuffer(userAsJSON))
 	if err != nil {
-		return newUser, err
+		return newUser, body, err
 	}
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if expectedStatusCode != http.StatusOK {
-		return newUser, err
+		body, _ = getResponseBody(resp)
+		return newUser, body, err
 	}
 	if err == nil {
 		err = render.DecodeJSON(resp.Body, &newUser)
+	} else {
+		body, _ = getResponseBody(resp)
 	}
 	if err == nil {
 		err = checkUser(user, newUser)
 	}
-	return newUser, err
+	return newUser, body, err
 }
 
 // UpdateUser updates an existing user and checks the received HTTP Status code against expectedStatusCode.
-func UpdateUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User, error) {
+func UpdateUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User, []byte, error) {
 	var newUser dataprovider.User
+	var body []byte
 	userAsJSON, err := json.Marshal(user)
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	req, err := http.NewRequest(http.MethodPut, httpBaseURL+userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	resp, err := getHTTPClient().Do(req)
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	body, _ = getResponseBody(resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if expectedStatusCode != http.StatusOK {
-		return newUser, err
+		return newUser, body, err
 	}
 	if err == nil {
-		newUser, err = GetUserByID(user.ID, expectedStatusCode)
+		newUser, body, err = GetUserByID(user.ID, expectedStatusCode)
 	}
 	if err == nil {
 		err = checkUser(user, newUser)
 	}
-	return newUser, err
+	return newUser, body, err
 }
 
 // RemoveUser removes an existing user and checks the received HTTP Status code against expectedStatusCode.
-func RemoveUser(user dataprovider.User, expectedStatusCode int) error {
+func RemoveUser(user dataprovider.User, expectedStatusCode int) ([]byte, error) {
+	var body []byte
 	req, err := http.NewRequest(http.MethodDelete, httpBaseURL+userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
 	if err != nil {
-		return err
+		return body, err
 	}
 	resp, err := getHTTPClient().Do(req)
 	if err != nil {
-		return err
+		return body, err
 	}
 	defer resp.Body.Close()
-	return checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	body, _ = getResponseBody(resp)
+	return body, checkResponse(resp.StatusCode, expectedStatusCode)
 }
 
 // GetUserByID gets an user by database id and checks the received HTTP Status code against expectedStatusCode.
-func GetUserByID(userID int64, expectedStatusCode int) (dataprovider.User, error) {
+func GetUserByID(userID int64, expectedStatusCode int) (dataprovider.User, []byte, error) {
 	var user dataprovider.User
+	var body []byte
 	resp, err := getHTTPClient().Get(httpBaseURL + userPath + "/" + strconv.FormatInt(userID, 10))
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &user)
+	} else {
+		body, _ = getResponseBody(resp)
 	}
-	return user, err
+	return user, body, err
 }
 
 // GetUsers allows to get a list of users and checks the received HTTP Status code against expectedStatusCode.
 // The number of results can be limited specifying a limit.
 // Some results can be skipped specifying an offset.
 // The results can be filtered specifying an username, the username filter is an exact match
-func GetUsers(limit int64, offset int64, username string, expectedStatusCode int) ([]dataprovider.User, error) {
+func GetUsers(limit int64, offset int64, username string, expectedStatusCode int) ([]dataprovider.User, []byte, error) {
 	var users []dataprovider.User
+	var body []byte
 	url, err := url.Parse(httpBaseURL + userPath)
 	if err != nil {
-		return users, err
+		return users, body, err
 	}
 	q := url.Query()
 	if limit > 0 {
@@ -141,87 +151,98 @@ func GetUsers(limit int64, offset int64, username string, expectedStatusCode int
 	url.RawQuery = q.Encode()
 	resp, err := getHTTPClient().Get(url.String())
 	if err != nil {
-		return users, err
+		return users, body, err
 	}
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &users)
+	} else {
+		body, _ = getResponseBody(resp)
 	}
-	return users, err
+	return users, body, err
 }
 
 // GetQuotaScans gets active quota scans and checks the received HTTP Status code against expectedStatusCode.
-func GetQuotaScans(expectedStatusCode int) ([]sftpd.ActiveQuotaScan, error) {
+func GetQuotaScans(expectedStatusCode int) ([]sftpd.ActiveQuotaScan, []byte, error) {
 	var quotaScans []sftpd.ActiveQuotaScan
+	var body []byte
 	resp, err := getHTTPClient().Get(httpBaseURL + quotaScanPath)
 	if err != nil {
-		return quotaScans, err
+		return quotaScans, body, err
 	}
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &quotaScans)
+	} else {
+		body, _ = getResponseBody(resp)
 	}
-	return quotaScans, err
+	return quotaScans, body, err
 }
 
 // StartQuotaScan start a new quota scan for the given user and checks the received HTTP Status code against expectedStatusCode.
-func StartQuotaScan(user dataprovider.User, expectedStatusCode int) error {
+func StartQuotaScan(user dataprovider.User, expectedStatusCode int) ([]byte, error) {
+	var body []byte
 	userAsJSON, err := json.Marshal(user)
 	if err != nil {
-		return err
+		return body, err
 	}
 	resp, err := getHTTPClient().Post(httpBaseURL+quotaScanPath, "application/json", bytes.NewBuffer(userAsJSON))
 	if err != nil {
-		return err
+		return body, err
 	}
 	defer resp.Body.Close()
-	return checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	body, _ = getResponseBody(resp)
+	return body, checkResponse(resp.StatusCode, expectedStatusCode)
 }
 
 // GetSFTPConnections returns status and stats for active SFTP connections
-func GetSFTPConnections(expectedStatusCode int) ([]sftpd.ConnectionStatus, error) {
+func GetSFTPConnections(expectedStatusCode int) ([]sftpd.ConnectionStatus, []byte, error) {
 	var connections []sftpd.ConnectionStatus
+	var body []byte
 	resp, err := getHTTPClient().Get(httpBaseURL + activeConnectionsPath)
 	if err != nil {
-		return connections, err
+		return connections, body, err
 	}
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &connections)
+	} else {
+		body, _ = getResponseBody(resp)
 	}
-	return connections, err
+	return connections, body, err
 }
 
 // CloseSFTPConnection closes an active SFTP connection identified by connectionID
-func CloseSFTPConnection(connectionID string, expectedStatusCode int) error {
+func CloseSFTPConnection(connectionID string, expectedStatusCode int) ([]byte, error) {
+	var body []byte
 	req, err := http.NewRequest(http.MethodDelete, httpBaseURL+activeConnectionsPath+"/"+connectionID, nil)
 	if err != nil {
-		return err
+		return body, err
 	}
 	resp, err := getHTTPClient().Do(req)
 	if err != nil {
-		return err
+		return body, err
 	}
 	defer resp.Body.Close()
-	return checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
+	body, _ = getResponseBody(resp)
+	return body, err
 }
 
-func checkResponse(actual int, expected int, resp *http.Response) error {
+func checkResponse(actual int, expected int) error {
 	if expected != actual {
 		return fmt.Errorf("wrong status code: got %v want %v", actual, expected)
 	}
-	if expected != http.StatusOK && resp != nil {
-		b, err := ioutil.ReadAll(resp.Body)
-		if err == nil {
-			logger.InfoToConsole("request: %v, response body: %v", resp.Request.URL, string(b))
-		}
-	}
 	return nil
 }
 
+func getResponseBody(resp *http.Response) ([]byte, error) {
+	return ioutil.ReadAll(resp.Body)
+}
+
 func checkUser(expected dataprovider.User, actual dataprovider.User) error {
 	if len(actual.Password) > 0 {
 		return errors.New("User password must not be visible")

+ 18 - 14
api/internal_test.go

@@ -28,11 +28,11 @@ func TestGetRespStatus(t *testing.T) {
 }
 
 func TestCheckResponse(t *testing.T) {
-	err := checkResponse(200, 201, nil)
+	err := checkResponse(http.StatusOK, http.StatusCreated)
 	if err == nil {
 		t.Errorf("check must fail")
 	}
-	err = checkResponse(400, 400, nil)
+	err = checkResponse(http.StatusBadRequest, http.StatusBadRequest)
 	if err != nil {
 		t.Errorf("test must succeed, error: %v", err)
 	}
@@ -146,15 +146,19 @@ func TestApiCallsWithBadURL(t *testing.T) {
 	oldBaseURL := httpBaseURL
 	SetBaseURL(invalidURL)
 	u := dataprovider.User{}
-	_, err := UpdateUser(u, http.StatusBadRequest)
+	_, _, err := UpdateUser(u, http.StatusBadRequest)
 	if err == nil {
 		t.Errorf("request with invalid URL must fail")
 	}
-	err = RemoveUser(u, http.StatusNotFound)
+	_, err = RemoveUser(u, http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request with invalid URL must fail")
 	}
-	err = CloseSFTPConnection("non_existent_id", http.StatusNotFound)
+	_, _, err = GetUsers(1, 0, "", http.StatusBadRequest)
+	if err == nil {
+		t.Errorf("request with invalid URL must fail")
+	}
+	_, err = CloseSFTPConnection("non_existent_id", http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request with invalid URL must fail")
 	}
@@ -165,39 +169,39 @@ func TestApiCallToNotListeningServer(t *testing.T) {
 	oldBaseURL := httpBaseURL
 	SetBaseURL(inactiveURL)
 	u := dataprovider.User{}
-	_, err := AddUser(u, http.StatusBadRequest)
+	_, _, err := AddUser(u, http.StatusBadRequest)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	_, err = UpdateUser(u, http.StatusNotFound)
+	_, _, err = UpdateUser(u, http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	err = RemoveUser(u, http.StatusNotFound)
+	_, err = RemoveUser(u, http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	_, err = GetUserByID(-1, http.StatusNotFound)
+	_, _, err = GetUserByID(-1, http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	_, err = GetUsers(100, 0, "", http.StatusOK)
+	_, _, err = GetUsers(100, 0, "", http.StatusOK)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	_, err = GetQuotaScans(http.StatusOK)
+	_, _, err = GetQuotaScans(http.StatusOK)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	err = StartQuotaScan(u, http.StatusNotFound)
+	_, err = StartQuotaScan(u, http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	_, err = GetSFTPConnections(http.StatusOK)
+	_, _, err = GetSFTPConnections(http.StatusOK)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}
-	err = CloseSFTPConnection("non_existent_id", http.StatusNotFound)
+	_, err = CloseSFTPConnection("non_existent_id", http.StatusNotFound)
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 	}

+ 63 - 63
sftpd/sftpd_test.go

@@ -157,7 +157,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u.QuotaSize = 6553600
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -184,7 +184,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 		if err != nil {
 			t.Errorf("file download error: %v", err)
 		}
-		user, err = api.GetUserByID(user.ID, http.StatusOK)
+		user, _, err = api.GetUserByID(user.ID, http.StatusOK)
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 		}
@@ -202,7 +202,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 		if err == nil {
 			t.Errorf("stat for deleted file must not succeed")
 		}
-		user, err = api.GetUserByID(user.ID, http.StatusOK)
+		user, _, err = api.GetUserByID(user.ID, http.StatusOK)
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 		}
@@ -213,7 +213,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 			t.Errorf("quota size does not match, expected: %v, actual: %v", expectedQuotaSize-testFileSize, user.UsedQuotaSize)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -221,7 +221,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 
 func TestDirCommands(t *testing.T) {
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -278,7 +278,7 @@ func TestDirCommands(t *testing.T) {
 			t.Errorf("remove missing path must fail")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -286,7 +286,7 @@ func TestDirCommands(t *testing.T) {
 
 func TestSymlink(t *testing.T) {
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -319,7 +319,7 @@ func TestSymlink(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -327,7 +327,7 @@ func TestSymlink(t *testing.T) {
 
 func TestStat(t *testing.T) {
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -375,7 +375,7 @@ func TestStat(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -384,7 +384,7 @@ func TestStat(t *testing.T) {
 // basic tests to verify virtual chroot, should be improved to cover more cases ...
 func TestEscapeHomeDir(t *testing.T) {
 	usePubKey := true
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -447,7 +447,7 @@ func TestEscapeHomeDir(t *testing.T) {
 		}
 		os.Remove(linkPath)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -457,7 +457,7 @@ func TestHomeSpecialChars(t *testing.T) {
 	usePubKey := true
 	u := getTestUser(usePubKey)
 	u.HomeDir = filepath.Join(homeBasePath, "abc açà#&%lk")
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -493,7 +493,7 @@ func TestHomeSpecialChars(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -502,7 +502,7 @@ func TestHomeSpecialChars(t *testing.T) {
 func TestLogin(t *testing.T) {
 	u := getTestUser(false)
 	u.PublicKey = []string{testPubKey}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -535,7 +535,7 @@ func TestLogin(t *testing.T) {
 	// testPubKey1 is not authorized
 	user.PublicKey = []string{testPubKey1}
 	user.Password = ""
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
@@ -547,7 +547,7 @@ func TestLogin(t *testing.T) {
 	// login a user with multiple public keys, only the second one is valid
 	user.PublicKey = []string{testPubKey1, testPubKey}
 	user.Password = ""
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
@@ -561,7 +561,7 @@ func TestLogin(t *testing.T) {
 			t.Errorf("sftp client with multiple public key must work if at least one public key is valid")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -569,14 +569,14 @@ func TestLogin(t *testing.T) {
 
 func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) {
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
 	user.Password = ""
 	user.PublicKey = []string{}
 	// password and public key should remain unchanged
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
@@ -594,7 +594,7 @@ func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) {
 			t.Errorf("unable to read remote dir: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -602,14 +602,14 @@ func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) {
 
 func TestLoginAfterUserUpdateEmptyPubKey(t *testing.T) {
 	usePubKey := true
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
 	user.Password = ""
 	user.PublicKey = []string{}
 	// password and public key should remain unchanged
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 	}
@@ -627,7 +627,7 @@ func TestLoginAfterUserUpdateEmptyPubKey(t *testing.T) {
 			t.Errorf("unable to read remote dir: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -637,7 +637,7 @@ func TestMaxSessions(t *testing.T) {
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u.MaxSessions = 1
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -659,7 +659,7 @@ func TestMaxSessions(t *testing.T) {
 			t.Errorf("max sessions exceeded, new login should not succeed")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -669,7 +669,7 @@ func TestQuotaFileReplace(t *testing.T) {
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u.QuotaFiles = 1000
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -691,7 +691,7 @@ func TestQuotaFileReplace(t *testing.T) {
 		if err != nil {
 			t.Errorf("file upload error: %v", err)
 		}
-		user, err = api.GetUserByID(user.ID, http.StatusOK)
+		user, _, err = api.GetUserByID(user.ID, http.StatusOK)
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 		}
@@ -700,7 +700,7 @@ func TestQuotaFileReplace(t *testing.T) {
 		if err != nil {
 			t.Errorf("file upload error: %v", err)
 		}
-		user, err = api.GetUserByID(user.ID, http.StatusOK)
+		user, _, err = api.GetUserByID(user.ID, http.StatusOK)
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 		}
@@ -713,7 +713,7 @@ func TestQuotaFileReplace(t *testing.T) {
 	}
 	// now set a quota size restriction and upload the same fail, upload should fail for space limit exceeded
 	user.QuotaSize = testFileSize - 1
-	user, err = api.UpdateUser(user, http.StatusOK)
+	user, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("error updating user: %v", err)
 	}
@@ -730,7 +730,7 @@ func TestQuotaFileReplace(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -738,7 +738,7 @@ func TestQuotaFileReplace(t *testing.T) {
 
 func TestQuotaScan(t *testing.T) {
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -761,31 +761,31 @@ func TestQuotaScan(t *testing.T) {
 			t.Errorf("file upload error: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
 	// create user with the same home dir, so there is at least an untracked file
-	user, err = api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err = api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
-	err = api.StartQuotaScan(user, http.StatusCreated)
+	_, err = api.StartQuotaScan(user, http.StatusCreated)
 	if err != nil {
 		t.Errorf("error starting quota scan: %v", err)
 	}
-	scans, err := api.GetQuotaScans(http.StatusOK)
+	scans, _, err := api.GetQuotaScans(http.StatusOK)
 	if err != nil {
 		t.Errorf("error getting active quota scans: %v", err)
 	}
 	for len(scans) > 0 {
-		scans, err = api.GetQuotaScans(http.StatusOK)
+		scans, _, err = api.GetQuotaScans(http.StatusOK)
 		if err != nil {
 			t.Errorf("error getting active quota scans: %v", err)
 			break
 		}
 	}
-	user, err = api.GetUserByID(user.ID, http.StatusOK)
+	user, _, err = api.GetUserByID(user.ID, http.StatusOK)
 	if err != nil {
 		t.Errorf("error getting user: %v", err)
 	}
@@ -795,7 +795,7 @@ func TestQuotaScan(t *testing.T) {
 	if expectedQuotaSize != user.UsedQuotaSize {
 		t.Errorf("quota size does not match after scan, expected: %v, actual: %v", expectedQuotaSize, user.UsedQuotaSize)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -817,7 +817,7 @@ func TestQuotaSize(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.QuotaFiles = 1
 	u.QuotaSize = testFileSize - 1
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -845,7 +845,7 @@ func TestQuotaSize(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -862,7 +862,7 @@ func TestBandwidthAndConnections(t *testing.T) {
 	// 100 ms tolerance
 	wantedUploadElapsed -= 100
 	wantedDownloadElapsed -= 100
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -916,7 +916,7 @@ func TestBandwidthAndConnections(t *testing.T) {
 			t.Errorf("connection closed upload must fail")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -925,7 +925,7 @@ func TestBandwidthAndConnections(t *testing.T) {
 func TestMissingFile(t *testing.T) {
 	usePubKey := false
 	u := getTestUser(usePubKey)
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -940,7 +940,7 @@ func TestMissingFile(t *testing.T) {
 			t.Errorf("download missing file must fail")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -949,7 +949,7 @@ func TestMissingFile(t *testing.T) {
 func TestOverwriteDirWithFile(t *testing.T) {
 	usePubKey := false
 	u := getTestUser(usePubKey)
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -991,7 +991,7 @@ func TestOverwriteDirWithFile(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1002,7 +1002,7 @@ func TestPermList(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1020,7 +1020,7 @@ func TestPermList(t *testing.T) {
 			t.Errorf("stat remote file without permission should not succeed")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1031,7 +1031,7 @@ func TestPermDownload(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDelete, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1061,7 +1061,7 @@ func TestPermDownload(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1072,7 +1072,7 @@ func TestPermUpload(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermDelete, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1093,7 +1093,7 @@ func TestPermUpload(t *testing.T) {
 			t.Errorf("file upload without permission should not succeed")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1104,7 +1104,7 @@ func TestPermDelete(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1129,7 +1129,7 @@ func TestPermDelete(t *testing.T) {
 			t.Errorf("delete without permission should not succeed")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1140,7 +1140,7 @@ func TestPermRename(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1169,7 +1169,7 @@ func TestPermRename(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1180,7 +1180,7 @@ func TestPermCreateDirs(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 		dataprovider.PermRename, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1205,7 +1205,7 @@ func TestPermCreateDirs(t *testing.T) {
 			t.Errorf("mkdir without permission should not succeed")
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1216,7 +1216,7 @@ func TestPermSymlink(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 		dataprovider.PermRename, dataprovider.PermCreateDirs}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1245,7 +1245,7 @@ func TestPermSymlink(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}
@@ -1253,7 +1253,7 @@ func TestPermSymlink(t *testing.T) {
 
 func TestSSHConnection(t *testing.T) {
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 	}
@@ -1261,7 +1261,7 @@ func TestSSHConnection(t *testing.T) {
 	if err == nil {
 		t.Errorf("ssh connection must fail: %v", err)
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 	}