diff --git a/api/harborapi_test.go b/api/harborapi_test.go index a07254930..44d5be69c 100644 --- a/api/harborapi_test.go +++ b/api/harborapi_test.go @@ -12,8 +12,6 @@ import ( "net/http/httptest" "path/filepath" "runtime" - // "strconv" - // "strings" "github.com/astaxie/beego" "github.com/dghubble/sling" @@ -63,7 +61,9 @@ func init() { beego.Router("/api/search/", &SearchAPI{}) beego.Router("/api/projects/", &ProjectAPI{}, "get:List;post:Post;head:Head") beego.Router("/api/projects/:id", &ProjectAPI{}, "delete:Delete;get:Get") + beego.Router("/api/users/?:id", &UserAPI{}) beego.Router("/api/users/:id([0-9]+)/password", &UserAPI{}, "put:ChangePassword") + beego.Router("/api/users/:id/sysadmin", &UserAPI{}, "put:ToggleUserAdminRole") beego.Router("/api/projects/:id/publicity", &ProjectAPI{}, "put:ToggleProjectPublic") beego.Router("/api/projects/:id([0-9]+)/logs/filter", &ProjectAPI{}, "post:FilterAccessLog") beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &ProjectMemberAPI{}, "get:Get;post:Post;delete:Delete;put:Put") @@ -157,30 +157,6 @@ func (a api) ProjectsPost(prjUsr usrInfo, project apilib.ProjectReq) (int, error return httpStatusCode, err } -//Change password -//Implementation Notes -//Change the password on a user that already exists. -//@param userID user ID -//@param password user old and new password -//@return error -//func (a api) UsersUserIDPasswordPut (user usrInfo, userID int32, password apilib.Password) int { -func (a api) UsersUserIDPasswordPut(user usrInfo, userID int32, password apilib.Password) int { - - _sling := sling.New().Put(a.basePath) - - // create path and map variables - path := "/api/users/" + fmt.Sprintf("%d", userID) + "/password" - fmt.Printf("change passwd path: %s\n", path) - fmt.Printf("password %+v\n", password) - _sling = _sling.Path(path) - - // body params - _sling = _sling.BodyJSON(password) - - httpStatusCode, _, _ := request(_sling, jsonAcceptHeader, user) - return httpStatusCode -} - func (a api) StatisticGet(user usrInfo) (apilib.StatisticMap, error) { _sling := sling.New().Get(a.basePath) @@ -741,21 +717,114 @@ func (a api) DeletePolicyByID(authInfo usrInfo, policyID string) (int, error) { //} //Get registered users of Harbor. -//func (a HarborApi) UsersGet (userName string) ([]User, error) { -//} +func (a api) UsersGet(userName string, authInfo usrInfo) (int, []apilib.User, error) { + _sling := sling.New().Get(a.basePath) + // create path and map variables + path := "/api/users/" + _sling = _sling.Path(path) + // body params + type QueryParams struct { + UserName string `url:"username, omitempty"` + } + _sling = _sling.QueryStruct(&QueryParams{UserName: userName}) + httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo) + var successPayLoad []apilib.User + if 200 == httpStatusCode && nil == err { + err = json.Unmarshal(body, &successPayLoad) + } + return httpStatusCode, successPayLoad, err +} + +//Get registered users by userid. +func (a api) UsersGetByID(userName string, authInfo usrInfo, userID int) (int, apilib.User, error) { + _sling := sling.New().Get(a.basePath) + // create path and map variables + path := "/api/users/" + fmt.Sprintf("%d", userID) + _sling = _sling.Path(path) + // body params + type QueryParams struct { + UserName string `url:"username, omitempty"` + } + _sling = _sling.QueryStruct(&QueryParams{UserName: userName}) + httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo) + var successPayLoad apilib.User + if 200 == httpStatusCode && nil == err { + err = json.Unmarshal(body, &successPayLoad) + } + return httpStatusCode, successPayLoad, err +} //Creates a new user account. -//func (a HarborApi) UsersPost (user User) (error) { -//} +func (a api) UsersPost(user apilib.User, authInfo ...usrInfo) (int, error) { + _sling := sling.New().Post(a.basePath) + + // create path and map variables + path := "/api/users/" + + _sling = _sling.Path(path) + + // body params + _sling = _sling.BodyJSON(user) + var httpStatusCode int + var err error + if len(authInfo) > 0 { + httpStatusCode, _, err = request(_sling, jsonAcceptHeader, authInfo[0]) + } else { + httpStatusCode, _, err = request(_sling, jsonAcceptHeader) + } + return httpStatusCode, err + +} + +//Update a registered user to change profile. +func (a api) UsersPut(userID int, profile apilib.UserProfile, authInfo usrInfo) (int, error) { + _sling := sling.New().Put(a.basePath) + // create path and map variables + path := "/api/users/" + fmt.Sprintf("%d", userID) + _sling = _sling.Path(path) + + // body params + _sling = _sling.BodyJSON(profile) + httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo) + return httpStatusCode, err +} + +//Update a registered user to be an administrator of Harbor. +func (a api) UsersToggleAdminRole(userID int, authInfo usrInfo, hasAdminRole int32) (int, error) { + _sling := sling.New().Put(a.basePath) + // create path and map variables + path := "/api/users/" + fmt.Sprintf("%d", userID) + "/sysadmin" + _sling = _sling.Path(path) + type QueryParams struct { + HasAdminRole int32 `json:"has_admin_role,omitempty"` + } + + _sling = _sling.BodyJSON(&QueryParams{HasAdminRole: hasAdminRole}) + httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo) + return httpStatusCode, err +} + +//Update password of a registered user. +func (a api) UsersUpdatePassword(userID int, password apilib.Password, authInfo usrInfo) (int, error) { + _sling := sling.New().Put(a.basePath) + // create path and map variables + path := "/api/users/" + fmt.Sprintf("%d", userID) + "/password" + _sling = _sling.Path(path) + // body params + _sling = _sling.BodyJSON(password) + httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo) + return httpStatusCode, err +} //Mark a registered user as be removed. -//func (a HarborApi) UsersUserIdDelete (userId int32) (error) { -//} - -//Update a registered user to change to be an administrator of Harbor. -//func (a HarborApi) UsersUserIdPut (userId int32) (error) { -//} - +func (a api) UsersDelete(userID int, authInfo usrInfo) (int, error) { + _sling := sling.New().Delete(a.basePath) + // create path and map variables + path := "/api/users/" + fmt.Sprintf("%d", userID) + _sling = _sling.Path(path) + httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo) + return httpStatusCode, err +} func updateInitPassword(userID int, password string) error { queryUser := models.User{UserID: userID} user, err := dao.GetUser(queryUser) diff --git a/api/log_test.go b/api/log_test.go index 549fd1a25..bb19a2731 100644 --- a/api/log_test.go +++ b/api/log_test.go @@ -17,7 +17,6 @@ func TestLogGet(t *testing.T) { //prepare for test - admin := &usrInfo{"admin", "Harbor12345"} var project apilib.ProjectReq project.ProjectName = "my_project" project.Public = 1 diff --git a/api/statistic_test.go b/api/statistic_test.go index 94c54b5ac..17e2b9f5a 100644 --- a/api/statistic_test.go +++ b/api/statistic_test.go @@ -21,8 +21,6 @@ func TestStatisticGet(t *testing.T) { //prepare for test - admin := &usrInfo{"admin", "Harbor12345"} - var myProCount, pubProCount, totalProCount int32 result, err := apiTest.StatisticGet(*admin) if err != nil { diff --git a/api/user.go b/api/user.go index 0355deff9..70b0796e9 100644 --- a/api/user.go +++ b/api/user.go @@ -315,13 +315,13 @@ func (ua *UserAPI) ToggleUserAdminRole() { // validate only validate when user register func validate(user models.User) error { - if isIllegalLength(user.Username, 0, 20) { + if isIllegalLength(user.Username, 1, 20) { return fmt.Errorf("Username with illegal length.") } if isContainIllegalChar(user.Username, []string{",", "~", "#", "$", "%"}) { return fmt.Errorf("Username contains illegal characters.") } - if isIllegalLength(user.Password, 0, 20) { + if isIllegalLength(user.Password, 7, 20) { return fmt.Errorf("Password with illegal length.") } if err := commonValidate(user); err != nil { diff --git a/api/user_test.go b/api/user_test.go new file mode 100644 index 000000000..7ea546990 --- /dev/null +++ b/api/user_test.go @@ -0,0 +1,398 @@ +package api + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/tests/apitests/apilib" + "testing" +) + +var testUser0002ID, testUser0003ID int +var testUser0002, testUser0003 apilib.User +var testUser0002Auth, testUser0003Auth *usrInfo + +func TestUsersPost(t *testing.T) { + + fmt.Println("Testing User Add") + + assert := assert.New(t) + apiTest := newHarborAPI() + + //case 1: register a new user without admin auth, expect 400, because self registration is on + fmt.Println("Register user without admin auth") + code, err := apiTest.UsersPost(testUser0002) + if err != nil { + t.Error("Error occured while add a test User", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 2: register a new user with admin auth, but username is empty, expect 400 + fmt.Println("Register user with admin auth, but username is empty") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 3: register a new user with admin auth, but bad username format, expect 400 + testUser0002.Username = "test@$" + fmt.Println("Register user with admin auth, but bad username format") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 4: register a new user with admin auth, but bad userpassword format, expect 400 + testUser0002.Username = "testUser0002" + fmt.Println("Register user with admin auth, but empty password.") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 5: register a new user with admin auth, but email is empty, expect 400 + testUser0002.Password = "testUser0002" + fmt.Println("Register user with admin auth, but email is empty") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 6: register a new user with admin auth, but bad email format, expect 400 + testUser0002.Email = "test..." + fmt.Println("Register user with admin auth, but bad email format") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 7: register a new user with admin auth, but userrealname is empty, expect 400 + /* + testUser0002.Email = "testUser0002@mydomain.com" + fmt.Println("Register user with admin auth, but user realname is empty") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + */ + //case 8: register a new user with admin auth, but bad userrealname format, expect 400 + testUser0002.Email = "testUser0002@mydomain.com" + testUser0002.Realname = "test$com" + fmt.Println("Register user with admin auth, but bad user realname format") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 9: register a new user with admin auth, but bad user comment, expect 400 + testUser0002.Realname = "testUser0002" + testUser0002.Comment = "vmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" + fmt.Println("Register user with admin auth, but bad user comment format") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Add user status should be 400") + } + + //case 10: register a new user with admin auth, expect 201 + fmt.Println("Register user with admin auth, right parameters") + testUser0002.Comment = "test user" + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(201, code, "Add user status should be 201") + } + + //case 11: register duplicate user with admin auth, expect 409 + fmt.Println("Register duplicate user with admin auth") + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(409, code, "Add user status should be 409") + } + + //case 12: register a new user with admin auth, but duplicate email, expect 409 + fmt.Println("Register user with admin auth, but duplicate email") + testUser0002.Username = "testUsertest" + testUser0002.Email = "testUser0002@mydomain.com" + code, err = apiTest.UsersPost(testUser0002, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(409, code, "Add user status should be 409") + } +} + +func TestUsersGet(t *testing.T) { + + fmt.Println("Testing User Get") + assert := assert.New(t) + apiTest := newHarborAPI() + + testUser0002.Username = "testUser0002" + //case 1: Get user2 with common auth, but no userid in path, expect 403 + + testUser0002Auth = &usrInfo{"testUser0002", "testUser0002"} + code, users, err := apiTest.UsersGet(testUser0002.Username, *testUser0002Auth) + if err != nil { + t.Error("Error occured while get users", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Get users status should be 403") + } + //case 2: Get user2 with admin auth, expect 200 + code, users, err = apiTest.UsersGet(testUser0002.Username, *admin) + if err != nil { + t.Error("Error occured while get users", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + assert.Equal(1, len(users), "Get users record should be 1 ") + testUser0002ID = users[0].UserId + } +} + +func TestUsersGetByID(t *testing.T) { + + fmt.Println("Testing User GetByID") + assert := assert.New(t) + apiTest := newHarborAPI() + + //case 1: Get user2 with userID and his own auth, expect 200 + code, user, err := apiTest.UsersGetByID(testUser0002.Username, *testUser0002Auth, testUser0002ID) + if err != nil { + t.Error("Error occured while get users", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + assert.Equal(testUser0002.Username, user.Username, "Get users username should be equal") + assert.Equal(testUser0002.Email, user.Email, "Get users email should be equal") + } + //case 2: Get user2 with user3 auth, expect 403 + testUser0003.Username = "testUser0003" + testUser0003.Email = "testUser0003@mydomain.com" + testUser0003.Password = "testUser0003" + testUser0003.Realname = "testUser0003" + code, err = apiTest.UsersPost(testUser0003, *admin) + if err != nil { + t.Error("Error occured while add a user", err.Error()) + t.Log(err) + } else { + assert.Equal(201, code, "Add user status should be 201") + } + + testUser0003Auth = &usrInfo{"testUser0003", "testUser0003"} + code, user, err = apiTest.UsersGetByID(testUser0002.Username, *testUser0003Auth, testUser0002ID) + if err != nil { + t.Error("Error occured while get users", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Get users status should be 403") + } + //case 3: Get user that does not exist with user2 auth, expect 404 not found. + code, user, err = apiTest.UsersGetByID(testUser0002.Username, *testUser0002Auth, 1000) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(404, code, "Get users status should be 404") + } + // Get user3ID in order to delete at the last of the test + code, users, err := apiTest.UsersGet(testUser0003.Username, *admin) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + assert.Equal(1, len(users), "Get users record should be 1") + testUser0003ID = users[0].UserId + } +} + +func TestUsersPut(t *testing.T) { + fmt.Println("Testing User Put") + assert := assert.New(t) + apiTest := newHarborAPI() + var profile apilib.UserProfile + //case 1: change user2 profile with user3 auth + code, err := apiTest.UsersPut(testUser0002ID, profile, *testUser0003Auth) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Get users status should be 403") + } + //case 2: change user2 profile with user2 auth, but bad parameters format. + code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Get users status should be 400") + } + //case 3: change user2 profile with user2 auth, but duplicate email. + profile.Realname = "test user" + profile.Email = "testUser0003@mydomain.com" + profile.Comment = "change profile" + code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(409, code, "Get users status should be 409") + } + //case 4: change user2 profile with user2 auth, right parameters format. + profile.Realname = "test user" + profile.Email = "testUser0002@vmware.com" + profile.Comment = "change profile" + code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + } +} + +func TestUsersToggleAdminRole(t *testing.T) { + fmt.Println("Testing Toggle User Admin Role") + assert := assert.New(t) + apiTest := newHarborAPI() + //case 1: toggle user2 admin role without admin auth + code, err := apiTest.UsersToggleAdminRole(testUser0002ID, *testUser0002Auth, int32(1)) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Get users status should be 403") + } + //case 2: toggle user2 admin role with admin auth + code, err = apiTest.UsersToggleAdminRole(testUser0002ID, *admin, int32(1)) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + } +} +func TestUsersUpdatePassword(t *testing.T) { + fmt.Println("Testing Update User Password") + assert := assert.New(t) + apiTest := newHarborAPI() + password := apilib.Password{OldPassword: "", NewPassword: ""} + //case 1: update user2 password with user3 auth + code, err := apiTest.UsersUpdatePassword(testUser0002ID, password, *testUser0003Auth) + if err != nil { + t.Error("Error occured while update user password", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Update user password status should be 403") + } + //case 2: update user2 password with admin auth, but oldpassword is empty + code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Get users status should be 400") + } + //case 3: update user2 password with admin auth, but oldpassword is wrong + password.OldPassword = "000" + code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Get users status should be 403") + } + //case 4: update user2 password with admin auth, but newpassword is empty + password.OldPassword = "testUser0002" + code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(400, code, "Get users status should be 400") + } + //case 5: update user2 password with admin auth, right parameters + password.NewPassword = "TestUser0002" + code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) + if err != nil { + t.Error("Error occured while change user profile", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + testUser0002.Password = password.NewPassword + testUser0002Auth.Passwd = password.NewPassword + } +} + +func TestUsersDelete(t *testing.T) { + + fmt.Println("Testing User Delete") + assert := assert.New(t) + apiTest := newHarborAPI() + + //case 1:delete user without admin auth + code, err := apiTest.UsersDelete(testUser0002ID, *testUser0003Auth) + if err != nil { + t.Error("Error occured while delete a testUser", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Delete testUser status should be 403") + } + //case 2: delete user with admin auth, user2 has already been toggled to admin, but can not delete himself + code, err = apiTest.UsersDelete(testUser0002ID, *testUser0002Auth) + if err != nil { + t.Error("Error occured while delete a testUser", err.Error()) + t.Log(err) + } else { + assert.Equal(403, code, "Delete testUser status should be 403") + } + //case 3: delete user with admin auth + code, err = apiTest.UsersDelete(testUser0002ID, *admin) + if err != nil { + t.Error("Error occured while delete a testUser", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Delete testUser status should be 200") + } + //delete user3 with admin auth + code, err = apiTest.UsersDelete(testUser0003ID, *admin) + if err != nil { + t.Error("Error occured while delete a testUser", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Delete testUser status should be 200") + } +} diff --git a/tests/apitests/apilib/user_profile.go b/tests/apitests/apilib/user_profile.go new file mode 100644 index 000000000..73fb82a0c --- /dev/null +++ b/tests/apitests/apilib/user_profile.go @@ -0,0 +1,31 @@ +/* + * Harbor API + * + * These APIs provide services for manipulating Harbor project. + * + * OpenAPI spec version: 0.3.0 + * + * Generated by: https://github.com/swagger-api/swagger-codegen.git + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package apilib + +type UserProfile struct { + Email string `json:"email,omitempty"` + + Realname string `json:"realname,omitempty"` + + Comment string `json:"comment,omitempty"` +}