浏览代码

api_utils: return response body too

useful for debug and to build external API, for example a CLI interface
Nicola Murino 6 年之前
父节点
当前提交
4dc52ee3e9
共有 4 个文件被更改,包括 225 次插入176 次删除
  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) {
 func TestBasicUserHandling(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -106,18 +106,18 @@ func TestBasicUserHandling(t *testing.T) {
 	user.QuotaFiles = 2
 	user.QuotaFiles = 2
 	user.UploadBandwidth = 128
 	user.UploadBandwidth = 128
 	user.DownloadBandwidth = 64
 	user.DownloadBandwidth = 64
-	user, err = api.UpdateUser(user, http.StatusOK)
+	user, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		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 {
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 		t.Errorf("unable to get users: %v", err)
 	}
 	}
 	if len(users) != 1 {
 	if len(users) != 1 {
 		t.Errorf("number of users mismatch, expected: 1, actual: %v", len(users))
 		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 {
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 		t.Errorf("unable to remove: %v", err)
 	}
 	}
@@ -127,7 +127,7 @@ func TestAddUserNoCredentials(t *testing.T) {
 	u := getTestUser()
 	u := getTestUser()
 	u.Password = ""
 	u.Password = ""
 	u.PublicKey = []string{}
 	u.PublicKey = []string{}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with no credentials: %v", err)
 		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) {
 func TestAddUserNoUsername(t *testing.T) {
 	u := getTestUser()
 	u := getTestUser()
 	u.Username = ""
 	u.Username = ""
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with no home dir: %v", err)
 		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) {
 func TestAddUserNoHomeDir(t *testing.T) {
 	u := getTestUser()
 	u := getTestUser()
 	u.HomeDir = ""
 	u.HomeDir = ""
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with no home dir: %v", err)
 		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) {
 func TestAddUserInvalidHomeDir(t *testing.T) {
 	u := getTestUser()
 	u := getTestUser()
 	u.HomeDir = "relative_path"
 	u.HomeDir = "relative_path"
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with invalid home dir: %v", err)
 		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) {
 func TestAddUserNoPerms(t *testing.T) {
 	u := getTestUser()
 	u := getTestUser()
 	u.Permissions = []string{}
 	u.Permissions = []string{}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with no perms: %v", err)
 		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) {
 func TestAddUserInvalidPerms(t *testing.T) {
 	u := getTestUser()
 	u := getTestUser()
 	u.Permissions = []string{"invalidPerm"}
 	u.Permissions = []string{"invalidPerm"}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with no perms: %v", err)
 		t.Errorf("unexpected error adding user with no perms: %v", err)
 	}
 	}
@@ -183,33 +183,33 @@ func TestUserPublicKey(t *testing.T) {
 	invalidPubKey := "invalid"
 	invalidPubKey := "invalid"
 	validPubKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
 	validPubKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
 	u.PublicKey = []string{invalidPubKey}
 	u.PublicKey = []string{invalidPubKey}
-	_, err := api.AddUser(u, http.StatusBadRequest)
+	_, _, err := api.AddUser(u, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error adding user with invalid pub key: %v", err)
 		t.Errorf("unexpected error adding user with invalid pub key: %v", err)
 	}
 	}
 	u.PublicKey = []string{validPubKey}
 	u.PublicKey = []string{validPubKey}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
 	user.PublicKey = []string{validPubKey, invalidPubKey}
 	user.PublicKey = []string{validPubKey, invalidPubKey}
-	_, err = api.UpdateUser(user, http.StatusBadRequest)
+	_, _, err = api.UpdateUser(user, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("update user with invalid public key must fail: %v", err)
 		t.Errorf("update user with invalid public key must fail: %v", err)
 	}
 	}
 	user.PublicKey = []string{validPubKey, validPubKey, validPubKey}
 	user.PublicKey = []string{validPubKey, validPubKey, validPubKey}
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		t.Errorf("unable to update user: %v", err)
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 		t.Errorf("unable to remove: %v", err)
 	}
 	}
 }
 }
 
 
 func TestUpdateUser(t *testing.T) {
 func TestUpdateUser(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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.Permissions = []string{dataprovider.PermCreateDirs, dataprovider.PermDelete, dataprovider.PermDownload}
 	user.UploadBandwidth = 1024
 	user.UploadBandwidth = 1024
 	user.DownloadBandwidth = 512
 	user.DownloadBandwidth = 512
-	user, err = api.UpdateUser(user, http.StatusOK)
+	user, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		t.Errorf("unable to update user: %v", err)
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 		t.Errorf("unable to remove: %v", err)
 	}
 	}
 }
 }
 
 
 func TestUpdateUserNoCredentials(t *testing.T) {
 func TestUpdateUserNoCredentials(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -241,157 +241,173 @@ func TestUpdateUserNoCredentials(t *testing.T) {
 	user.PublicKey = []string{}
 	user.PublicKey = []string{}
 	// password and public key will be omitted from json serialization if empty and so they will remain unchanged
 	// 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
 	// and no validation error will be raised
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error updating user with no credentials: %v", err)
 		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 {
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 		t.Errorf("unable to remove: %v", err)
 	}
 	}
 }
 }
 
 
 func TestUpdateUserEmptyHomeDir(t *testing.T) {
 func TestUpdateUserEmptyHomeDir(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
 	user.HomeDir = ""
 	user.HomeDir = ""
-	_, err = api.UpdateUser(user, http.StatusBadRequest)
+	_, _, err = api.UpdateUser(user, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error updating user with empty home dir: %v", err)
 		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 {
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 		t.Errorf("unable to remove: %v", err)
 	}
 	}
 }
 }
 
 
 func TestUpdateUserInvalidHomeDir(t *testing.T) {
 func TestUpdateUserInvalidHomeDir(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
 	user.HomeDir = "relative_path"
 	user.HomeDir = "relative_path"
-	_, err = api.UpdateUser(user, http.StatusBadRequest)
+	_, _, err = api.UpdateUser(user, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error updating user with empty home dir: %v", err)
 		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 {
 	if err != nil {
 		t.Errorf("unable to remove: %v", err)
 		t.Errorf("unable to remove: %v", err)
 	}
 	}
 }
 }
 
 
 func TestUpdateNonExistentUser(t *testing.T) {
 func TestUpdateNonExistentUser(t *testing.T) {
-	_, err := api.UpdateUser(getTestUser(), http.StatusNotFound)
+	_, _, err := api.UpdateUser(getTestUser(), http.StatusNotFound)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		t.Errorf("unable to update user: %v", err)
 	}
 	}
 }
 }
 
 
 func TestGetNonExistentUser(t *testing.T) {
 func TestGetNonExistentUser(t *testing.T) {
-	_, err := api.GetUserByID(0, http.StatusNotFound)
+	_, _, err := api.GetUserByID(0, http.StatusNotFound)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to get user: %v", err)
 		t.Errorf("unable to get user: %v", err)
 	}
 	}
 }
 }
 
 
 func TestDeleteNonExistentUser(t *testing.T) {
 func TestDeleteNonExistentUser(t *testing.T) {
-	err := api.RemoveUser(getTestUser(), http.StatusNotFound)
+	_, err := api.RemoveUser(getTestUser(), http.StatusNotFound)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
 }
 }
 
 
 func TestAddDuplicateUser(t *testing.T) {
 func TestAddDuplicateUser(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
-	_, err = api.AddUser(getTestUser(), http.StatusInternalServerError)
+	_, _, err = api.AddUser(getTestUser(), http.StatusInternalServerError)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add second user: %v", err)
 		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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
 }
 }
 
 
 func TestGetUsers(t *testing.T) {
 func TestGetUsers(t *testing.T) {
-	user1, err := api.AddUser(getTestUser(), http.StatusOK)
+	user1, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
 	u := getTestUser()
 	u := getTestUser()
 	u.Username = defaultUsername + "1"
 	u.Username = defaultUsername + "1"
-	user2, err := api.AddUser(u, http.StatusOK)
+	user2, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add second user: %v", err)
 		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 {
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 		t.Errorf("unable to get users: %v", err)
 	}
 	}
 	if len(users) < 2 {
 	if len(users) < 2 {
 		t.Errorf("at least 2 users are expected")
 		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 {
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 		t.Errorf("unable to get users: %v", err)
 	}
 	}
 	if len(users) != 1 {
 	if len(users) != 1 {
 		t.Errorf("1 user is expected")
 		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 {
 	if err != nil {
 		t.Errorf("unable to get users: %v", err)
 		t.Errorf("unable to get users: %v", err)
 	}
 	}
 	if len(users) != 1 {
 	if len(users) != 1 {
 		t.Errorf("1 user is expected")
 		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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
-	err = api.RemoveUser(user2, http.StatusOK)
+	_, err = api.RemoveUser(user2, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
 }
 }
 
 
 func TestGetQuotaScans(t *testing.T) {
 func TestGetQuotaScans(t *testing.T) {
-	_, err := api.GetQuotaScans(http.StatusOK)
+	_, _, err := api.GetQuotaScans(http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to get quota scans: %v", err)
 		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) {
 func TestStartQuotaScan(t *testing.T) {
-	user, err := api.AddUser(getTestUser(), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
-	err = api.StartQuotaScan(user, http.StatusCreated)
+	_, err = api.StartQuotaScan(user, http.StatusCreated)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to start quota scan: %v", err)
 		t.Errorf("unable to start quota scan: %v", err)
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
 }
 }
 
 
 func TestGetSFTPConnections(t *testing.T) {
 func TestGetSFTPConnections(t *testing.T) {
-	_, err := api.GetSFTPConnections(http.StatusOK)
+	_, _, err := api.GetSFTPConnections(http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to get sftp connections: %v", err)
 		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) {
 func TestCloseActiveSFTPConnection(t *testing.T) {
-	err := api.CloseSFTPConnection("non_existent_id", http.StatusNotFound)
+	_, err := api.CloseSFTPConnection("non_existent_id", http.StatusNotFound)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unexpected error closing non existent sftp connection: %v", err)
 		t.Errorf("unexpected error closing non existent sftp connection: %v", err)
 	}
 	}
@@ -597,6 +613,14 @@ func TestStartQuotaScanMock(t *testing.T) {
 	if err == nil {
 	if err == nil {
 		os.Remove(user.HomeDir)
 		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)
 	userAsJSON = getUserAsJSON(t, user)
 	req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
 	req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
 	rr = executeRequest(req)
 	rr = executeRequest(req)

+ 74 - 53
api/api_utils.go

@@ -12,15 +12,13 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/drakkan/sftpgo/dataprovider"
 	"github.com/drakkan/sftpgo/dataprovider"
-	"github.com/drakkan/sftpgo/logger"
 	"github.com/drakkan/sftpgo/sftpd"
 	"github.com/drakkan/sftpgo/sftpd"
 	"github.com/drakkan/sftpgo/utils"
 	"github.com/drakkan/sftpgo/utils"
 	"github.com/go-chi/render"
 	"github.com/go-chi/render"
 )
 )
 
 
 var (
 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"
 // 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.
 // 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 newUser dataprovider.User
+	var body []byte
 	userAsJSON, err := json.Marshal(user)
 	userAsJSON, err := json.Marshal(user)
 	if err != nil {
 	if err != nil {
-		return newUser, err
+		return newUser, body, err
 	}
 	}
 	resp, err := getHTTPClient().Post(httpBaseURL+userPath, "application/json", bytes.NewBuffer(userAsJSON))
 	resp, err := getHTTPClient().Post(httpBaseURL+userPath, "application/json", bytes.NewBuffer(userAsJSON))
 	if err != nil {
 	if err != nil {
-		return newUser, err
+		return newUser, body, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if expectedStatusCode != http.StatusOK {
 	if expectedStatusCode != http.StatusOK {
-		return newUser, err
+		body, _ = getResponseBody(resp)
+		return newUser, body, err
 	}
 	}
 	if err == nil {
 	if err == nil {
 		err = render.DecodeJSON(resp.Body, &newUser)
 		err = render.DecodeJSON(resp.Body, &newUser)
+	} else {
+		body, _ = getResponseBody(resp)
 	}
 	}
 	if err == nil {
 	if err == nil {
 		err = checkUser(user, newUser)
 		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.
 // 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 newUser dataprovider.User
+	var body []byte
 	userAsJSON, err := json.Marshal(user)
 	userAsJSON, err := json.Marshal(user)
 	if err != nil {
 	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))
 	req, err := http.NewRequest(http.MethodPut, httpBaseURL+userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
 	if err != nil {
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	}
 	resp, err := getHTTPClient().Do(req)
 	resp, err := getHTTPClient().Do(req)
 	if err != nil {
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	body, _ = getResponseBody(resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if expectedStatusCode != http.StatusOK {
 	if expectedStatusCode != http.StatusOK {
-		return newUser, err
+		return newUser, body, err
 	}
 	}
 	if err == nil {
 	if err == nil {
-		newUser, err = GetUserByID(user.ID, expectedStatusCode)
+		newUser, body, err = GetUserByID(user.ID, expectedStatusCode)
 	}
 	}
 	if err == nil {
 	if err == nil {
 		err = checkUser(user, newUser)
 		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.
 // 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)
 	req, err := http.NewRequest(http.MethodDelete, httpBaseURL+userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
 	if err != nil {
 	if err != nil {
-		return err
+		return body, err
 	}
 	}
 	resp, err := getHTTPClient().Do(req)
 	resp, err := getHTTPClient().Do(req)
 	if err != nil {
 	if err != nil {
-		return err
+		return body, err
 	}
 	}
 	defer resp.Body.Close()
 	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.
 // 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 user dataprovider.User
+	var body []byte
 	resp, err := getHTTPClient().Get(httpBaseURL + userPath + "/" + strconv.FormatInt(userID, 10))
 	resp, err := getHTTPClient().Get(httpBaseURL + userPath + "/" + strconv.FormatInt(userID, 10))
 	if err != nil {
 	if err != nil {
-		return user, err
+		return user, body, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &user)
 		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.
 // 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.
 // The number of results can be limited specifying a limit.
 // Some results can be skipped specifying an offset.
 // Some results can be skipped specifying an offset.
 // The results can be filtered specifying an username, the username filter is an exact match
 // 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 users []dataprovider.User
+	var body []byte
 	url, err := url.Parse(httpBaseURL + userPath)
 	url, err := url.Parse(httpBaseURL + userPath)
 	if err != nil {
 	if err != nil {
-		return users, err
+		return users, body, err
 	}
 	}
 	q := url.Query()
 	q := url.Query()
 	if limit > 0 {
 	if limit > 0 {
@@ -141,87 +151,98 @@ func GetUsers(limit int64, offset int64, username string, expectedStatusCode int
 	url.RawQuery = q.Encode()
 	url.RawQuery = q.Encode()
 	resp, err := getHTTPClient().Get(url.String())
 	resp, err := getHTTPClient().Get(url.String())
 	if err != nil {
 	if err != nil {
-		return users, err
+		return users, body, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &users)
 		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.
 // 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 quotaScans []sftpd.ActiveQuotaScan
+	var body []byte
 	resp, err := getHTTPClient().Get(httpBaseURL + quotaScanPath)
 	resp, err := getHTTPClient().Get(httpBaseURL + quotaScanPath)
 	if err != nil {
 	if err != nil {
-		return quotaScans, err
+		return quotaScans, body, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &quotaScans)
 		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.
 // 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)
 	userAsJSON, err := json.Marshal(user)
 	if err != nil {
 	if err != nil {
-		return err
+		return body, err
 	}
 	}
 	resp, err := getHTTPClient().Post(httpBaseURL+quotaScanPath, "application/json", bytes.NewBuffer(userAsJSON))
 	resp, err := getHTTPClient().Post(httpBaseURL+quotaScanPath, "application/json", bytes.NewBuffer(userAsJSON))
 	if err != nil {
 	if err != nil {
-		return err
+		return body, err
 	}
 	}
 	defer resp.Body.Close()
 	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
 // 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 connections []sftpd.ConnectionStatus
+	var body []byte
 	resp, err := getHTTPClient().Get(httpBaseURL + activeConnectionsPath)
 	resp, err := getHTTPClient().Get(httpBaseURL + activeConnectionsPath)
 	if err != nil {
 	if err != nil {
-		return connections, err
+		return connections, body, err
 	}
 	}
 	defer resp.Body.Close()
 	defer resp.Body.Close()
-	err = checkResponse(resp.StatusCode, expectedStatusCode, resp)
+	err = checkResponse(resp.StatusCode, expectedStatusCode)
 	if err == nil && expectedStatusCode == http.StatusOK {
 	if err == nil && expectedStatusCode == http.StatusOK {
 		err = render.DecodeJSON(resp.Body, &connections)
 		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
 // 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)
 	req, err := http.NewRequest(http.MethodDelete, httpBaseURL+activeConnectionsPath+"/"+connectionID, nil)
 	if err != nil {
 	if err != nil {
-		return err
+		return body, err
 	}
 	}
 	resp, err := getHTTPClient().Do(req)
 	resp, err := getHTTPClient().Do(req)
 	if err != nil {
 	if err != nil {
-		return err
+		return body, err
 	}
 	}
 	defer resp.Body.Close()
 	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 {
 	if expected != actual {
 		return fmt.Errorf("wrong status code: got %v want %v", actual, expected)
 		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
 	return nil
 }
 }
 
 
+func getResponseBody(resp *http.Response) ([]byte, error) {
+	return ioutil.ReadAll(resp.Body)
+}
+
 func checkUser(expected dataprovider.User, actual dataprovider.User) error {
 func checkUser(expected dataprovider.User, actual dataprovider.User) error {
 	if len(actual.Password) > 0 {
 	if len(actual.Password) > 0 {
 		return errors.New("User password must not be visible")
 		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) {
 func TestCheckResponse(t *testing.T) {
-	err := checkResponse(200, 201, nil)
+	err := checkResponse(http.StatusOK, http.StatusCreated)
 	if err == nil {
 	if err == nil {
 		t.Errorf("check must fail")
 		t.Errorf("check must fail")
 	}
 	}
-	err = checkResponse(400, 400, nil)
+	err = checkResponse(http.StatusBadRequest, http.StatusBadRequest)
 	if err != nil {
 	if err != nil {
 		t.Errorf("test must succeed, error: %v", err)
 		t.Errorf("test must succeed, error: %v", err)
 	}
 	}
@@ -146,15 +146,19 @@ func TestApiCallsWithBadURL(t *testing.T) {
 	oldBaseURL := httpBaseURL
 	oldBaseURL := httpBaseURL
 	SetBaseURL(invalidURL)
 	SetBaseURL(invalidURL)
 	u := dataprovider.User{}
 	u := dataprovider.User{}
-	_, err := UpdateUser(u, http.StatusBadRequest)
+	_, _, err := UpdateUser(u, http.StatusBadRequest)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request with invalid URL must fail")
 		t.Errorf("request with invalid URL must fail")
 	}
 	}
-	err = RemoveUser(u, http.StatusNotFound)
+	_, err = RemoveUser(u, http.StatusNotFound)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request with invalid URL must fail")
 		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 {
 	if err == nil {
 		t.Errorf("request with invalid URL must fail")
 		t.Errorf("request with invalid URL must fail")
 	}
 	}
@@ -165,39 +169,39 @@ func TestApiCallToNotListeningServer(t *testing.T) {
 	oldBaseURL := httpBaseURL
 	oldBaseURL := httpBaseURL
 	SetBaseURL(inactiveURL)
 	SetBaseURL(inactiveURL)
 	u := dataprovider.User{}
 	u := dataprovider.User{}
-	_, err := AddUser(u, http.StatusBadRequest)
+	_, _, err := AddUser(u, http.StatusBadRequest)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	_, err = UpdateUser(u, http.StatusNotFound)
+	_, _, err = UpdateUser(u, http.StatusNotFound)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	err = RemoveUser(u, http.StatusNotFound)
+	_, err = RemoveUser(u, http.StatusNotFound)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	_, err = GetUserByID(-1, http.StatusNotFound)
+	_, _, err = GetUserByID(-1, http.StatusNotFound)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	_, err = GetUsers(100, 0, "", http.StatusOK)
+	_, _, err = GetUsers(100, 0, "", http.StatusOK)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	_, err = GetQuotaScans(http.StatusOK)
+	_, _, err = GetQuotaScans(http.StatusOK)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	err = StartQuotaScan(u, http.StatusNotFound)
+	_, err = StartQuotaScan(u, http.StatusNotFound)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		t.Errorf("request to an inactive URL must fail")
 	}
 	}
-	_, err = GetSFTPConnections(http.StatusOK)
+	_, _, err = GetSFTPConnections(http.StatusOK)
 	if err == nil {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		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 {
 	if err == nil {
 		t.Errorf("request to an inactive URL must fail")
 		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
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.QuotaSize = 6553600
 	u.QuotaSize = 6553600
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -184,7 +184,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 		if err != nil {
 		if err != nil {
 			t.Errorf("file download error: %v", err)
 			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 {
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 			t.Errorf("error getting user: %v", err)
 		}
 		}
@@ -202,7 +202,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 		if err == nil {
 		if err == nil {
 			t.Errorf("stat for deleted file must not succeed")
 			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 {
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 			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)
 			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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -221,7 +221,7 @@ func TestBasicSFTPHandling(t *testing.T) {
 
 
 func TestDirCommands(t *testing.T) {
 func TestDirCommands(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -278,7 +278,7 @@ func TestDirCommands(t *testing.T) {
 			t.Errorf("remove missing path must fail")
 			t.Errorf("remove missing path must fail")
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -286,7 +286,7 @@ func TestDirCommands(t *testing.T) {
 
 
 func TestSymlink(t *testing.T) {
 func TestSymlink(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -327,7 +327,7 @@ func TestSymlink(t *testing.T) {
 
 
 func TestStat(t *testing.T) {
 func TestStat(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		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 ...
 // basic tests to verify virtual chroot, should be improved to cover more cases ...
 func TestEscapeHomeDir(t *testing.T) {
 func TestEscapeHomeDir(t *testing.T) {
 	usePubKey := true
 	usePubKey := true
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -447,7 +447,7 @@ func TestEscapeHomeDir(t *testing.T) {
 		}
 		}
 		os.Remove(linkPath)
 		os.Remove(linkPath)
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -457,7 +457,7 @@ func TestHomeSpecialChars(t *testing.T) {
 	usePubKey := true
 	usePubKey := true
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.HomeDir = filepath.Join(homeBasePath, "abc açà#&%lk")
 	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 {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -502,7 +502,7 @@ func TestHomeSpecialChars(t *testing.T) {
 func TestLogin(t *testing.T) {
 func TestLogin(t *testing.T) {
 	u := getTestUser(false)
 	u := getTestUser(false)
 	u.PublicKey = []string{testPubKey}
 	u.PublicKey = []string{testPubKey}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -535,7 +535,7 @@ func TestLogin(t *testing.T) {
 	// testPubKey1 is not authorized
 	// testPubKey1 is not authorized
 	user.PublicKey = []string{testPubKey1}
 	user.PublicKey = []string{testPubKey1}
 	user.Password = ""
 	user.Password = ""
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		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
 	// login a user with multiple public keys, only the second one is valid
 	user.PublicKey = []string{testPubKey1, testPubKey}
 	user.PublicKey = []string{testPubKey1, testPubKey}
 	user.Password = ""
 	user.Password = ""
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		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")
 			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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -569,14 +569,14 @@ func TestLogin(t *testing.T) {
 
 
 func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) {
 func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
 	user.Password = ""
 	user.Password = ""
 	user.PublicKey = []string{}
 	user.PublicKey = []string{}
 	// password and public key should remain unchanged
 	// password and public key should remain unchanged
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		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)
 			t.Errorf("unable to read remote dir: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -602,14 +602,14 @@ func TestLoginAfterUserUpdateEmptyPwd(t *testing.T) {
 
 
 func TestLoginAfterUserUpdateEmptyPubKey(t *testing.T) {
 func TestLoginAfterUserUpdateEmptyPubKey(t *testing.T) {
 	usePubKey := true
 	usePubKey := true
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
 	user.Password = ""
 	user.Password = ""
 	user.PublicKey = []string{}
 	user.PublicKey = []string{}
 	// password and public key should remain unchanged
 	// password and public key should remain unchanged
-	_, err = api.UpdateUser(user, http.StatusOK)
+	_, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to update user: %v", err)
 		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)
 			t.Errorf("unable to read remote dir: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -637,7 +637,7 @@ func TestMaxSessions(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.MaxSessions = 1
 	u.MaxSessions = 1
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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")
 			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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -669,7 +669,7 @@ func TestQuotaFileReplace(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.QuotaFiles = 1000
 	u.QuotaFiles = 1000
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -691,7 +691,7 @@ func TestQuotaFileReplace(t *testing.T) {
 		if err != nil {
 		if err != nil {
 			t.Errorf("file upload error: %v", err)
 			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 {
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 			t.Errorf("error getting user: %v", err)
 		}
 		}
@@ -700,7 +700,7 @@ func TestQuotaFileReplace(t *testing.T) {
 		if err != nil {
 		if err != nil {
 			t.Errorf("file upload error: %v", err)
 			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 {
 		if err != nil {
 			t.Errorf("error getting user: %v", err)
 			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
 	// now set a quota size restriction and upload the same fail, upload should fail for space limit exceeded
 	user.QuotaSize = testFileSize - 1
 	user.QuotaSize = testFileSize - 1
-	user, err = api.UpdateUser(user, http.StatusOK)
+	user, _, err = api.UpdateUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("error updating user: %v", err)
 		t.Errorf("error updating user: %v", err)
 	}
 	}
@@ -730,7 +730,7 @@ func TestQuotaFileReplace(t *testing.T) {
 			t.Errorf("error removing uploaded file: %v", err)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -738,7 +738,7 @@ func TestQuotaFileReplace(t *testing.T) {
 
 
 func TestQuotaScan(t *testing.T) {
 func TestQuotaScan(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -761,31 +761,31 @@ func TestQuotaScan(t *testing.T) {
 			t.Errorf("file upload error: %v", err)
 			t.Errorf("file upload error: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
 	// create user with the same home dir, so there is at least an untracked file
 	// 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 {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
-	err = api.StartQuotaScan(user, http.StatusCreated)
+	_, err = api.StartQuotaScan(user, http.StatusCreated)
 	if err != nil {
 	if err != nil {
 		t.Errorf("error starting quota scan: %v", err)
 		t.Errorf("error starting quota scan: %v", err)
 	}
 	}
-	scans, err := api.GetQuotaScans(http.StatusOK)
+	scans, _, err := api.GetQuotaScans(http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("error getting active quota scans: %v", err)
 		t.Errorf("error getting active quota scans: %v", err)
 	}
 	}
 	for len(scans) > 0 {
 	for len(scans) > 0 {
-		scans, err = api.GetQuotaScans(http.StatusOK)
+		scans, _, err = api.GetQuotaScans(http.StatusOK)
 		if err != nil {
 		if err != nil {
 			t.Errorf("error getting active quota scans: %v", err)
 			t.Errorf("error getting active quota scans: %v", err)
 			break
 			break
 		}
 		}
 	}
 	}
-	user, err = api.GetUserByID(user.ID, http.StatusOK)
+	user, _, err = api.GetUserByID(user.ID, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("error getting user: %v", err)
 		t.Errorf("error getting user: %v", err)
 	}
 	}
@@ -795,7 +795,7 @@ func TestQuotaScan(t *testing.T) {
 	if expectedQuotaSize != user.UsedQuotaSize {
 	if expectedQuotaSize != user.UsedQuotaSize {
 		t.Errorf("quota size does not match after scan, expected: %v, actual: %v", 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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -817,7 +817,7 @@ func TestQuotaSize(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.QuotaFiles = 1
 	u.QuotaFiles = 1
 	u.QuotaSize = testFileSize - 1
 	u.QuotaSize = testFileSize - 1
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -862,7 +862,7 @@ func TestBandwidthAndConnections(t *testing.T) {
 	// 100 ms tolerance
 	// 100 ms tolerance
 	wantedUploadElapsed -= 100
 	wantedUploadElapsed -= 100
 	wantedDownloadElapsed -= 100
 	wantedDownloadElapsed -= 100
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -916,7 +916,7 @@ func TestBandwidthAndConnections(t *testing.T) {
 			t.Errorf("connection closed upload must fail")
 			t.Errorf("connection closed upload must fail")
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -925,7 +925,7 @@ func TestBandwidthAndConnections(t *testing.T) {
 func TestMissingFile(t *testing.T) {
 func TestMissingFile(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -940,7 +940,7 @@ func TestMissingFile(t *testing.T) {
 			t.Errorf("download missing file must fail")
 			t.Errorf("download missing file must fail")
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -949,7 +949,7 @@ func TestMissingFile(t *testing.T) {
 func TestOverwriteDirWithFile(t *testing.T) {
 func TestOverwriteDirWithFile(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1002,7 +1002,7 @@ func TestPermList(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete, dataprovider.PermRename,
 	u.Permissions = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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")
 			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 {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1031,7 +1031,7 @@ func TestPermDownload(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDelete, dataprovider.PermRename,
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDelete, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1072,7 +1072,7 @@ func TestPermUpload(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermDelete, dataprovider.PermRename,
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermDelete, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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")
 			t.Errorf("file upload without permission should not succeed")
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1104,7 +1104,7 @@ func TestPermDelete(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermRename,
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermRename,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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")
 			t.Errorf("delete without permission should not succeed")
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1140,7 +1140,7 @@ func TestPermRename(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
 		dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1180,7 +1180,7 @@ func TestPermCreateDirs(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 		dataprovider.PermRename, dataprovider.PermCreateSymlinks}
 		dataprovider.PermRename, dataprovider.PermCreateSymlinks}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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")
 			t.Errorf("mkdir without permission should not succeed")
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1216,7 +1216,7 @@ func TestPermSymlink(t *testing.T) {
 	u := getTestUser(usePubKey)
 	u := getTestUser(usePubKey)
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 	u.Permissions = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDelete,
 		dataprovider.PermRename, dataprovider.PermCreateDirs}
 		dataprovider.PermRename, dataprovider.PermCreateDirs}
-	user, err := api.AddUser(u, http.StatusOK)
+	user, _, err := api.AddUser(u, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		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)
 			t.Errorf("error removing uploaded file: %v", err)
 		}
 		}
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}
@@ -1253,7 +1253,7 @@ func TestPermSymlink(t *testing.T) {
 
 
 func TestSSHConnection(t *testing.T) {
 func TestSSHConnection(t *testing.T) {
 	usePubKey := false
 	usePubKey := false
-	user, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
+	user, _, err := api.AddUser(getTestUser(usePubKey), http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to add user: %v", err)
 		t.Errorf("unable to add user: %v", err)
 	}
 	}
@@ -1261,7 +1261,7 @@ func TestSSHConnection(t *testing.T) {
 	if err == nil {
 	if err == nil {
 		t.Errorf("ssh connection must fail: %v", err)
 		t.Errorf("ssh connection must fail: %v", err)
 	}
 	}
-	err = api.RemoveUser(user, http.StatusOK)
+	_, err = api.RemoveUser(user, http.StatusOK)
 	if err != nil {
 	if err != nil {
 		t.Errorf("unable to remove user: %v", err)
 		t.Errorf("unable to remove user: %v", err)
 	}
 	}