Merge pull request #4611 from stonezdj/change_rest_api_pm

Change project member REST API
This commit is contained in:
Daniel Jiang 2018-04-11 18:42:29 +08:00 committed by GitHub
commit 04335de72b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 297 additions and 1073 deletions

View File

@ -448,186 +448,7 @@ paths:
description: Project or metadata does not exist.
'500':
description: Internal server errors.
'/projects/{project_id}/members/':
get:
summary: Return a project's relevant role members.
description: >
This endpoint is for user to search a specified project's relevant role
members.
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
tags:
- Products
responses:
'200':
description: Get project's relevant role members successfully.
schema:
type: array
items:
$ref: '#/definitions/User'
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the project.
'404':
description: Project ID does not exist.
'500':
description: Unexpected internal errors.
post:
summary: Add project role member accompany with relevant project and user.
description: >
This endpoint is for user to add project role member accompany with
relevant project and user.
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
- name: roles
in: body
description: >-
Role members for adding to relevant project. Only one role is
supported in the role list.
schema:
$ref: '#/definitions/RoleParam'
tags:
- Products
responses:
'200':
description: Role members added to relevant project successfully.
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the project.
'404':
description: Project ID or username does not exist.
'409':
description: User has already added as a project role member.
'415':
$ref: '#/responses/UnsupportedMediaType'
'500':
description: Unexpected internal errors.
'/projects/{project_id}/members/{user_id}':
get:
summary: Return role members accompany with relevant project and user.
description: >
This endpoint is for user to get role members accompany with relevant
project and user.
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID
- name: user_id
in: path
type: integer
format: int
required: true
description: Relevant user ID
tags:
- Products
responses:
'200':
description: Get project role members successfully.
schema:
type: array
items:
$ref: '#/definitions/Role'
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the project.
'404':
description: Project ID does not exist.
'500':
description: Unexpected internal errors.
put:
summary: Update project role members accompany with relevant project and user.
description: >
This endpoint is for user to update current project role members
accompany with relevant project and user.
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
- name: user_id
in: path
type: integer
format: int
required: true
description: Relevant user ID.
- name: roles
in: body
schema:
$ref: '#/definitions/RoleParam'
description: Updates for roles and username.
tags:
- Products
responses:
'200':
description: Project role members updated successfully.
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the project.
'404':
description: Project ID does not exist.
'500':
description: Unexpected internal errors.
delete:
summary: Delete project role members accompany with relevant project and user.
description: >
This endpoint is aimed to remove project role members already added to
the relevant project and user.
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
- name: user_id
in: path
type: integer
format: int
required: true
description: Relevant user ID.
tags:
- Products
responses:
'200':
description: Project role members deleted successfully.
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the project.
'404':
description: Project ID does not exist.
'500':
description: Unexpected internal errors.
'/projects/{project_id}/projectmembers':
'/projects/{project_id}/members':
get:
summary: Get all project member information
description: Get all project member information
@ -691,7 +512,7 @@ paths:
description: Project does not exist.
'500':
description: Unexpected internal errors.
'/projects/{project_id}/projectmembers/{mid}':
'/projects/{project_id}/members/{mid}':
get:
summary: Get the project member information
description: Get the project member information
@ -2492,6 +2313,71 @@ paths:
$ref: '#/definitions/UserGroup'
'500':
description: Unexpected internal errors.
/ldap/users/search:
get:
summary: Search available ldap users.
description: >
This endpoint searches the available ldap users based on related
configuration parameters. Support searched by input ladp configuration,
load configuration from the system and specific filter.
parameters:
- name: username
in: query
type: string
required: false
description: Registered user ID
tags:
- Products
responses:
'200':
description: Search ldap users successfully.
schema:
type: array
items:
$ref: '#/definitions/LdapUsers'
'401':
description: User need to login first.
'403':
description: Only admin has this authority.
'500':
description: Unexpected internal errors.
/ldap/users/import:
post:
summary: Import selected available ldap users.
description: >
This endpoint adds the selected available ldap users to harbor based on
related configuration parameters from the system. System will try to
guess the user email address and realname, add to harbor user
information.
If have errors when import user, will return the list of importing
failed uid and the failed reason.
parameters:
- name: uid_list
in: body
description: >-
The uid listed for importing. This list will check users validity of
ldap service based on configuration from the system.
required: true
schema:
$ref: '#/definitions/LdapImportUsers'
tags:
- Products
responses:
'200':
description: Add ldap users successfully.
'401':
description: User need to login first.
'403':
description: Only admin has this authority.
'415':
$ref: '#/responses/UnsupportedMediaType'
'404':
description: Failed import some users.
schema:
type: array
items:
$ref: '#/definitions/LdapFailedImportUsers'
/usergroups:
get:
summary: Get all user groups information
@ -2613,71 +2499,7 @@ paths:
description: Only admin has this authority.
'500':
description: Unexpected internal errors.
/ldap/users/search:
get:
summary: Search available ldap users.
description: >
This endpoint searches the available ldap users based on related
configuration parameters. Support searched by input ladp configuration,
load configuration from the system and specific filter.
parameters:
- name: username
in: query
type: string
required: false
description: Registered user ID
tags:
- Products
responses:
'200':
description: Search ldap users successfully.
schema:
type: array
items:
$ref: '#/definitions/LdapUsers'
'401':
description: User need to login first.
'403':
description: Only admin has this authority.
'500':
description: Unexpected internal errors.
/ldap/users/import:
post:
summary: Import selected available ldap users.
description: >
This endpoint adds the selected available ldap users to harbor based on
related configuration parameters from the system. System will try to
guess the user email address and realname, add to harbor user
information.
If have errors when import user, will return the list of importing
failed uid and the failed reason.
parameters:
- name: uid_list
in: body
description: >-
The uid listed for importing. This list will check users validity of
ldap service based on configuration from the system.
required: true
schema:
$ref: '#/definitions/LdapImportUsers'
tags:
- Products
responses:
'200':
description: Add ldap users successfully.
'401':
description: User need to login first.
'403':
description: Only admin has this authority.
'415':
$ref: '#/responses/UnsupportedMediaType'
'404':
description: Failed import some users.
schema:
type: array
items:
$ref: '#/definitions/LdapFailedImportUsers'
/configurations:
get:
summary: Get system configurations.
@ -3799,4 +3621,3 @@ definitions:
description: The DN of the LDAP group if group type is 1 (LDAP group).

View File

@ -635,31 +635,6 @@ func TestGetProjectById(t *testing.T) {
}
}
func TestGetUserByProject(t *testing.T) {
pid := currentProject.ProjectID
u1 := models.User{
Username: "Tester",
}
u2 := models.User{
Username: "nononono",
}
users, err := GetUserByProject(pid, u1)
if err != nil {
t.Errorf("Error happened in GetUserByProject: %v, project Id: %d, user: %+v", err, pid, u1)
}
if len(users) != 1 {
t.Errorf("unexpected length of user list, expected: 1, the users list: %+v", users)
}
users, err = GetUserByProject(pid, u2)
if err != nil {
t.Errorf("Error happened in GetUserByProject: %v, project Id: %d, user: %+v", err, pid, u2)
}
if len(users) != 0 {
t.Errorf("unexpected length of user list, expected: 0, the users list: %+v", users)
}
}
func TestGetUserProjectRoles(t *testing.T) {
r, err := GetUserProjectRoles(currentUser.UserID, currentProject.ProjectID, common.UserMember)
if err != nil {
@ -700,68 +675,6 @@ func TestGetProjects(t *testing.T) {
}
}
func TestAddProjectMember(t *testing.T) {
pmid, err := AddProjectMember(currentProject.ProjectID, 1, models.DEVELOPER, common.UserMember)
if err != nil {
t.Errorf("Error occurred in AddProjectMember: %v", err)
}
if pmid == 0 {
t.Errorf("Error add project member, pmid=0")
}
pmid, err = AddProjectMember(currentProject.ProjectID, 1, models.DEVELOPER, "randomstring")
if err == nil {
t.Errorf("Should failed on adding project member when adding duplicated items")
}
roles, err := GetUserProjectRoles(1, currentProject.ProjectID, common.UserMember)
if err != nil {
t.Errorf("Error occurred in GetUserProjectRoles: %v", err)
}
flag := false
for _, role := range roles {
if role.Name == "developer" {
flag = true
break
}
}
if !flag {
t.Errorf("the user which ID is 1 does not have developer privileges")
}
}
func TestUpdateProjectMember(t *testing.T) {
err := UpdateProjectMember(currentProject.ProjectID, 1, models.GUEST, "randomstring")
if err != nil {
t.Errorf("Error occurred in UpdateProjectMember: %v", err)
}
roles, err := GetUserProjectRoles(1, currentProject.ProjectID, common.UserMember)
if err != nil {
t.Errorf("Error occurred in GetUserProjectRoles: %v", err)
}
if roles[0].Name != "guest" {
t.Errorf("The user with ID 1 is not guest role after update, the acutal role: %s", roles[0].Name)
}
}
func TestDeleteProjectMember(t *testing.T) {
err := DeleteProjectMember(currentProject.ProjectID, 1, "randomstring")
if err != nil {
t.Errorf("Error occurred in DeleteProjectMember: %v", err)
}
roles, err := GetUserProjectRoles(1, currentProject.ProjectID, common.UserMember)
if err != nil {
t.Errorf("Error occurred in GetUserProjectRoles: %v", err)
}
if len(roles) != 0 {
t.Errorf("delete record failed from table project_member")
}
}
func TestGetRoleByID(t *testing.T) {
r, err := GetRoleByID(models.PROJECTADMIN)
if err != nil {

View File

@ -17,14 +17,12 @@ package dao
import (
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
"fmt"
"time"
//"github.com/vmware/harbor/src/common/utils/log"
)
//TODO:transaction, return err
// AddProject adds a project to the database along with project roles information and access log records.
func AddProject(project models.Project) (int64, error) {
@ -45,13 +43,47 @@ func AddProject(project models.Project) (int64, error) {
return 0, err
}
pmID, err := AddProjectMember(projectID, project.OwnerID, models.PROJECTADMIN, common.UserMember)
pmID, err := addProjectMember(models.Member{
ProjectID: projectID,
EntityID: project.OwnerID,
Role: models.PROJECTADMIN,
EntityType: common.UserMember,
})
if err != nil {
return 0, err
}
if pmID == 0 {
return projectID, fmt.Errorf("Failed to add project member, pmid=0")
}
return projectID, err
}
func addProjectMember(member models.Member) (int, error) {
log.Debugf("Adding project member %+v", member)
o := GetOrmer()
if member.EntityID <= 0 {
return 0, fmt.Errorf("Invalid entity_id, member: %+v", member)
}
if member.ProjectID <= 0 {
return 0, fmt.Errorf("Invalid project_id, member: %+v", member)
}
sql := "insert into project_member (project_id, entity_id , role, entity_type) values (?, ?, ?, ?)"
r, err := o.Raw(sql, member.ProjectID, member.EntityID, member.Role, member.EntityType).Exec()
if err != nil {
return 0, err
}
pmid, err := r.LastInsertId()
if err != nil {
return 0, err
}
return int(pmid), err
}
// GetProjectByID ...
func GetProjectByID(id int64) (*models.Project, error) {
o := GetOrmer()

View File

@ -74,7 +74,6 @@ func GetProjectMember(queryMember models.Member) ([]*models.Member, error) {
func AddProjectMember(member models.Member) (int, error) {
log.Debugf("Adding project member %+v", member)
o := dao.GetOrmer()
if member.EntityID <= 0 {
@ -85,6 +84,11 @@ func AddProjectMember(member models.Member) (int, error) {
return 0, fmt.Errorf("Invalid project_id, member: %+v", member)
}
delSQL := "delete from project_member where project_id = ? and entity_id = ? and entity_type = ? "
_, err := o.Raw(delSQL, member.ProjectID, member.EntityID, member.EntityType).Exec()
if err != nil {
return 0, err
}
sql := "insert into project_member (project_id, entity_id , role, entity_type) values (?, ?, ?, ?)"
r, err := o.Raw(sql, member.ProjectID, member.EntityID, member.Role, member.EntityType).Exec()
if err != nil {
@ -99,7 +103,6 @@ func AddProjectMember(member models.Member) (int, error) {
// UpdateProjectMemberRole updates the record in table project_member, only role can be changed
func UpdateProjectMemberRole(pmID int, role int) error {
o := dao.GetOrmer()
sql := "update project_member set role = ? where id = ? "
_, err := o.Raw(sql, role, pmID).Exec()

View File

@ -1,95 +0,0 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// 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 dao
import (
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
)
// AddProjectMember inserts a record to table project_member
func AddProjectMember(projectID int64, userID int, role int, entityType string) (int, error) {
o := GetOrmer()
if !(entityType == common.UserMember || entityType == common.GroupMember) {
entityType = common.UserMember
}
sql := `insert into project_member (project_id, entity_id , role, entity_type) values (?, ?, ?, ?)`
_, err := o.Raw(sql, projectID, userID, role, entityType).Exec()
if err != nil {
return 0, err
}
var pmid int
querySQL := `select id from project_member where project_id = ? and entity_id = ? and entity_type = ? limit 1`
err = o.Raw(querySQL, projectID, userID, entityType).QueryRow(&pmid)
if err != nil {
return 0, err
}
return pmid, err
}
// UpdateProjectMember updates the record in table project_member
func UpdateProjectMember(projectID int64, userID int, role int, entityType string) error {
o := GetOrmer()
if !(entityType == common.UserMember || entityType == common.GroupMember) {
entityType = common.UserMember
}
sql := `update project_member set role = ? where project_id = ? and entity_id = ?`
_, err := o.Raw(sql, role, projectID, userID).Exec()
return err
}
// DeleteProjectMember delete the record from table project_member
func DeleteProjectMember(projectID int64, userID int, entityType string) error {
o := GetOrmer()
if !(entityType == common.UserMember || entityType == common.GroupMember) {
entityType = common.UserMember
}
sql := `delete from project_member where project_id = ? and entity_id = ? and entity_type = ?`
if _, err := o.Raw(sql, projectID, userID, entityType).Exec(); err != nil {
return err
}
return nil
}
// GetUserByProject gets all members of the project.
func GetUserByProject(projectID int64, queryUser models.User) ([]*models.UserMember, error) {
o := GetOrmer()
sql := `select pm.id as id, u.user_id, u.username, u.creation_time, u.update_time, r.name as rolename,
r.role_id as role, pm.entity_type as entity_type from user u join project_member pm
on pm.project_id = ? and u.user_id = pm.entity_id
join role r on pm.role = r.role_id where u.deleted = 0 and pm.entity_type = 'u' `
queryParam := make([]interface{}, 1)
queryParam = append(queryParam, projectID)
if len(queryUser.Username) != 0 {
sql += ` and u.username like ? `
queryParam = append(queryParam, `%`+Escape(queryUser.Username)+`%`)
}
sql += ` order by u.username `
members := []*models.UserMember{}
_, err := o.Raw(sql, queryParam).QueryRows(&members)
return members, err
}

View File

@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/dao/project"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/promgr"
@ -122,25 +123,39 @@ func TestMain(m *testing.M) {
private.ProjectID = id
defer dao.DeleteProject(id)
var projectAdminPMID, developerUserPMID, guestUserPMID int
// add project members
_, err = dao.AddProjectMember(private.ProjectID, projectAdminUser.UserID, common.RoleProjectAdmin, common.UserMember)
projectAdminPMID, err = project.AddProjectMember(models.Member{
ProjectID: private.ProjectID,
EntityID: projectAdminUser.UserID,
EntityType: common.UserMember,
Role: common.RoleProjectAdmin,
})
if err != nil {
log.Fatalf("failed to add member: %v", err)
}
defer dao.DeleteProjectMember(private.ProjectID, projectAdminUser.UserID, common.UserMember)
defer project.DeleteProjectMemberByID(projectAdminPMID)
_, err = dao.AddProjectMember(private.ProjectID, developerUser.UserID, common.RoleDeveloper, common.UserMember)
developerUserPMID, err = project.AddProjectMember(models.Member{
ProjectID: private.ProjectID,
EntityID: developerUser.UserID,
EntityType: common.UserMember,
Role: common.RoleDeveloper,
})
if err != nil {
log.Fatalf("failed to add member: %v", err)
}
defer dao.DeleteProjectMember(private.ProjectID, developerUser.UserID, common.UserMember)
_, err = dao.AddProjectMember(private.ProjectID, guestUser.UserID, common.RoleGuest, common.UserMember)
defer project.DeleteProjectMemberByID(developerUserPMID)
guestUserPMID, err = project.AddProjectMember(models.Member{
ProjectID: private.ProjectID,
EntityID: guestUser.UserID,
EntityType: common.UserMember,
Role: common.RoleGuest,
})
if err != nil {
log.Fatalf("failed to add member: %v", err)
}
defer dao.DeleteProjectMember(private.ProjectID, guestUser.UserID, common.UserMember)
defer project.DeleteProjectMemberByID(guestUserPMID)
os.Exit(m.Run())
}

View File

@ -31,13 +31,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/dao/project"
common_http "github.com/vmware/harbor/src/common/http"
"github.com/vmware/harbor/src/common/models"
)
var (
nonSysAdminID, projAdminID, projDeveloperID, projGuestID int
nonSysAdminID, projAdminID, projDeveloperID, projGuestID int64
projAdminPMID, projDeveloperPMID, projGuestPMID int
// The following users/credentials are registered and assigned roles at the beginning of
// running testing and cleaned up at the end.
// Do not try to change the system and project roles that the users have during
@ -206,7 +207,8 @@ func TestMain(m *testing.M) {
func prepare() error {
// register nonSysAdmin
id, err := dao.Register(models.User{
var err error
nonSysAdminID, err = dao.Register(models.User{
Username: nonSysAdmin.Name,
Password: nonSysAdmin.Passwd,
Email: nonSysAdmin.Name + "@test.com",
@ -214,10 +216,9 @@ func prepare() error {
if err != nil {
return err
}
nonSysAdminID = int(id)
// register projAdmin and assign project admin role
id, err = dao.Register(models.User{
projAdminID, err = dao.Register(models.User{
Username: projAdmin.Name,
Password: projAdmin.Passwd,
Email: projAdmin.Name + "@test.com",
@ -225,14 +226,18 @@ func prepare() error {
if err != nil {
return err
}
projAdminID = int(id)
if _, err = dao.AddProjectMember(1, projAdminID, models.PROJECTADMIN, common.UserMember); err != nil {
if projAdminPMID, err = project.AddProjectMember(models.Member{
ProjectID: 1,
Role: models.PROJECTADMIN,
EntityID: int(projAdminID),
EntityType: common.UserMember,
}); err != nil {
return err
}
// register projDeveloper and assign project developer role
id, err = dao.Register(models.User{
projDeveloperID, err = dao.Register(models.User{
Username: projDeveloper.Name,
Password: projDeveloper.Passwd,
Email: projDeveloper.Name + "@test.com",
@ -240,14 +245,18 @@ func prepare() error {
if err != nil {
return err
}
projDeveloperID = int(id)
if _, err = dao.AddProjectMember(1, projDeveloperID, models.DEVELOPER, common.UserMember); err != nil {
if projDeveloperPMID, err = project.AddProjectMember(models.Member{
ProjectID: 1,
Role: models.DEVELOPER,
EntityID: int(projDeveloperID),
EntityType: common.UserMember,
}); err != nil {
return err
}
// register projGuest and assign project guest role
id, err = dao.Register(models.User{
projGuestID, err = dao.Register(models.User{
Username: projGuest.Name,
Password: projGuest.Passwd,
Email: projGuest.Name + "@test.com",
@ -255,23 +264,29 @@ func prepare() error {
if err != nil {
return err
}
projGuestID = int(id)
_, err = dao.AddProjectMember(1, projGuestID, models.GUEST, common.UserMember)
if projGuestPMID, err = project.AddProjectMember(models.Member{
ProjectID: 1,
Role: models.GUEST,
EntityID: int(projGuestID),
EntityType: common.UserMember,
}); err != nil {
return err
}
return err
}
func clean() {
ids := []int{projAdminID, projDeveloperID, projGuestID}
for _, id := range ids {
if err := dao.DeleteProjectMember(1, id, common.UserMember); err != nil {
pmids := []int{projAdminPMID, projDeveloperPMID, projGuestPMID}
for _, id := range pmids {
if err := project.DeleteProjectMemberByID(id); err != nil {
fmt.Printf("failed to clean up member %d from project library: %v", id, err)
}
}
ids = append(ids, nonSysAdminID)
for _, id := range ids {
if err := dao.DeleteUser(id); err != nil {
userids := []int64{nonSysAdminID, projAdminID, projDeveloperID, projGuestID}
for _, id := range userids {
if err := dao.DeleteUser(int(id)); err != nil {
fmt.Printf("failed to clean up user %d: %v \n", id, err)
}
}

View File

@ -104,11 +104,10 @@ func init() {
beego.Router("/api/users/:id/sysadmin", &UserAPI{}, "put:ToggleUserAdminRole")
beego.Router("/api/projects/:id([0-9]+)/logs", &ProjectAPI{}, "get:Logs")
beego.Router("/api/projects/:id([0-9]+)/_deletable", &ProjectAPI{}, "get:Deletable")
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &ProjectUserMemberAPI{}, "get:Get;post:Post;delete:Delete;put:Put")
beego.Router("/api/projects/:id([0-9]+)/metadatas/?:name", &MetadataAPI{}, "get:Get")
beego.Router("/api/projects/:id([0-9]+)/metadatas/", &MetadataAPI{}, "post:Post")
beego.Router("/api/projects/:id([0-9]+)/metadatas/:name", &MetadataAPI{}, "put:Put;delete:Delete")
beego.Router("/api/projects/:pid([0-9]+)/projectmembers/?:pmid([0-9]+)", &ProjectMemberAPI{})
beego.Router("/api/projects/:pid([0-9]+)/members/?:pmid([0-9]+)", &ProjectMemberAPI{})
beego.Router("/api/repositories", &RepositoryAPI{})
beego.Router("/api/statistics", &StatisticAPI{})
beego.Router("/api/users/?:id", &UserAPI{})
@ -440,12 +439,13 @@ func (a testapi) GetProjectMembersByProID(prjUsr usrInfo, projectID string) (int
}
//Add project role member accompany with projectID
func (a testapi) AddProjectMember(prjUsr usrInfo, projectID string, roles apilib.RoleParam) (int, error) {
//func (a testapi) AddProjectMember(prjUsr usrInfo, projectID string, roles apilib.RoleParam) (int, error) {
func (a testapi) AddProjectMember(prjUsr usrInfo, projectID string, member *models.MemberReq) (int, error) {
_sling := sling.New().Post(a.basePath)
path := "/api/projects/" + projectID + "/members/"
_sling = _sling.Path(path)
_sling = _sling.BodyJSON(roles)
_sling = _sling.BodyJSON(member)
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, prjUsr)
return httpStatusCode, err

View File

@ -1,271 +0,0 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// 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 api
import (
"fmt"
"net/http"
"strings"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/auth"
)
// ProjectUserMemberAPI handles request to /api/projects/{}/members/{}
type ProjectUserMemberAPI struct {
BaseController
memberID int
currentUserID int
project *models.Project
}
type memberReq struct {
Username string `json:"username"`
UserID int `json:"user_id"`
Roles []int `json:"roles"`
}
// Prepare validates the URL and parms
func (pma *ProjectUserMemberAPI) Prepare() {
pma.BaseController.Prepare()
if !pma.SecurityCtx.IsAuthenticated() {
pma.HandleUnauthorized()
return
}
user, err := dao.GetUser(models.User{
Username: pma.SecurityCtx.GetUsername(),
})
if err != nil {
pma.HandleInternalServerError(
fmt.Sprintf("failed to get user %s: %v",
pma.SecurityCtx.GetUsername(), err))
return
}
pma.currentUserID = user.UserID
pid, err := pma.GetInt64FromPath(":pid")
if err != nil || pid <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", pid)
}
pma.HandleBadRequest(text)
return
}
project, err := pma.ProjectMgr.Get(pid)
if err != nil {
pma.ParseAndHandleError(fmt.Sprintf("failed to get project %d", pid), err)
return
}
if project == nil {
pma.HandleNotFound(fmt.Sprintf("project %d not found", pid))
return
}
pma.project = project
if !(pma.Ctx.Input.IsGet() && pma.SecurityCtx.HasReadPerm(pid) ||
pma.SecurityCtx.HasAllPerm(pid)) {
pma.HandleForbidden(pma.SecurityCtx.GetUsername())
return
}
if len(pma.GetStringFromPath(":mid")) != 0 {
mid, err := pma.GetInt64FromPath(":mid")
if err != nil || mid <= 0 {
text := "invalid member ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", mid)
}
pma.HandleBadRequest(text)
return
}
member, err := dao.GetUser(models.User{
UserID: int(mid),
})
if err != nil {
pma.HandleInternalServerError(fmt.Sprintf("failed to get user %d: %v", mid, err))
return
}
if member == nil {
pma.HandleNotFound(fmt.Sprintf("member %d not found", mid))
return
}
pma.memberID = member.UserID
}
}
// Get ...
func (pma *ProjectUserMemberAPI) Get() {
pid := pma.project.ProjectID
if pma.memberID == 0 { //member id not set return list of the members
username := pma.GetString("username")
queryUser := models.User{Username: username}
userList, err := dao.GetUserByProject(pid, queryUser)
if err != nil {
log.Errorf("Failed to query database for member list, error: %v", err)
pma.RenderError(http.StatusInternalServerError, "Internal Server Error")
return
}
pma.Data["json"] = userList
} else { //return detail of a member
roleList, err := listRoles(pma.memberID, pid, common.UserMember)
if err != nil {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
if len(roleList) == 0 {
pma.CustomAbort(http.StatusNotFound, fmt.Sprintf("user %d is not a member of the project", pma.memberID))
}
//return empty role list to indicate if a user is not a member
result := make(map[string]interface{})
user, err := dao.GetUser(models.User{UserID: pma.memberID})
if err != nil {
log.Errorf("Error occurred in GetUser, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
result["username"] = user.Username
result["user_id"] = pma.memberID
result["roles"] = roleList
pma.Data["json"] = result
}
pma.ServeJSON()
}
// Post ...
func (pma *ProjectUserMemberAPI) Post() {
projectID := pma.project.ProjectID
var req memberReq
pma.DecodeJSONReq(&req)
username := strings.TrimSpace(req.Username)
userID := checkUserExists(username)
if userID <= 0 {
user, err := auth.SearchUser(username)
if err != nil {
log.Errorf("Failed the search user, error: %v", err)
pma.RenderError(http.StatusInternalServerError, "Failed to search user")
return
}
if user == nil {
log.Errorf("Current user doesn't exist: %v", username)
pma.RenderError(http.StatusNotFound, "Failed to search user: "+username)
return
}
err = auth.OnBoardUser(user)
if err != nil {
log.Errorf("Failed the onboard user, error: %s", err)
pma.RenderError(http.StatusInternalServerError, "Failed to onboard user")
return
}
if user.UserID <= 0 {
log.Error("Failed the onboard user, UserId <=0")
pma.RenderError(http.StatusInternalServerError, "Failed to onboard user")
return
}
userID = user.UserID
}
rolelist, err := dao.GetUserProjectRoles(userID, projectID, common.UserMember)
if err != nil {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
if len(rolelist) > 0 {
log.Warningf("user is already added to project, user id: %d, project id: %d", userID, projectID)
pma.RenderError(http.StatusConflict, "user is ready in project")
return
}
if len(req.Roles) <= 0 || len(req.Roles) > 1 {
pma.CustomAbort(http.StatusBadRequest, "only one role is supported")
}
rid := req.Roles[0]
if !(rid == models.PROJECTADMIN ||
rid == models.DEVELOPER ||
rid == models.GUEST) {
pma.CustomAbort(http.StatusBadRequest, "invalid role")
}
_, err = dao.AddProjectMember(projectID, userID, rid, common.UserMember)
if err != nil {
log.Errorf("Failed to update DB to add project user role, project id: %d, user id: %d, role id: %d", projectID, userID, rid)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in database")
return
}
}
// Put ...
func (pma *ProjectUserMemberAPI) Put() {
pid := pma.project.ProjectID
mid := pma.memberID
var req memberReq
pma.DecodeJSONReq(&req)
roleList, err := dao.GetUserProjectRoles(mid, pid, common.UserMember)
if len(roleList) == 0 {
log.Warningf("User is not in project, user id: %d, project id: %d", mid, pid)
pma.RenderError(http.StatusNotFound, "user not exist in project")
return
}
//TODO: delete and insert should in one transaction
//delete user project role record for the given user
err = dao.DeleteProjectMember(pid, mid, common.UserMember)
if err != nil {
log.Errorf("Failed to delete project roles for user, user id: %d, project id: %d, error: %v", mid, pid, err)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB")
return
}
//insert roles in request
for _, rid := range req.Roles {
_, err = dao.AddProjectMember(pid, mid, int(rid), common.UserMember)
if err != nil {
log.Errorf("Failed to update DB to add project user role, project id: %d, user id: %d, role id: %d", pid, mid, rid)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in database")
return
}
}
}
// Delete ...
func (pma *ProjectUserMemberAPI) Delete() {
pid := pma.project.ProjectID
mid := pma.memberID
err := dao.DeleteProjectMember(pid, mid, common.UserMember)
if err != nil {
log.Errorf("Failed to delete project roles for user, user id: %d, project id: %d, error: %v", mid, pid, err)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB")
return
}
}

View File

@ -1,213 +0,0 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// 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 api
import (
"fmt"
"testing"
"strconv"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/tests/apitests/apilib"
)
func TestMemGet(t *testing.T) {
var result []apilib.User
var httpStatusCode int
var err error
assert := assert.New(t)
apiTest := newHarborAPI()
projectID := "1"
fmt.Println("Testing Member Get API")
//-------------------case 1 : response code = 200------------------------//
httpStatusCode, result, err = apiTest.GetProjectMembersByProID(*admin, projectID)
if err != nil {
t.Error("Error whihle get members by projectID", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
assert.Equal("proj_admin", result[0].Username, "User name should be proj_admin")
}
//---------case 2: Response Code=401,User need to log in first.----------//
fmt.Println("case 2: Response Code=401,User need to log in first.")
httpStatusCode, result, err = apiTest.GetProjectMembersByProID(*unknownUsr, projectID)
if err != nil {
t.Error("Error while get members by projectID", err.Error())
t.Log(err)
} else {
assert.Equal(int(401), httpStatusCode, "Case 2: Project creation status should be 401")
}
//------------case 3: Response Code=404,Project does not exist-----------//
fmt.Println("case 3: Response Code=404,Project does not exist")
projectID = "11"
httpStatusCode, result, err = apiTest.GetProjectMembersByProID(*admin, projectID)
if err != nil {
t.Error("Error while get members by projectID", err.Error())
t.Log(err)
} else {
assert.Equal(int(404), httpStatusCode, "Case 3: Project creation status should be 404")
}
//------------case 4: Response Code=404, member does not exist-----------//
fmt.Println("case 4: Response Code=404, member does not exist")
projectID = "1"
memberID := "10000"
httpStatusCode, err = apiTest.GetMemByPIDUID(*admin, projectID, memberID)
if err != nil {
t.Fatalf("failed to get member %s of project %s: %v", memberID, projectID, err)
}
assert.Equal(int(404), httpStatusCode,
fmt.Sprintf("response status code should be 404 other than %d", httpStatusCode))
fmt.Printf("\n")
}
/**
* Add project role member accompany with projectID
* role_id = 1 : ProjectAdmin
* role_id = 2 : Developer
* role_id = 3 : Guest
*/
func TestMemPost(t *testing.T) {
var httpStatusCode int
var err error
assert := assert.New(t)
apiTest := newHarborAPI()
projectID := "1"
CommonAddUser()
roles := &apilib.RoleParam{[]int32{1}, TestUserName}
fmt.Printf("Add User \"%s\" successfully!\n", TestUserName)
fmt.Println("Testing Member Post API")
//-------------------case 1 : response code = 200------------------------//
fmt.Println("case 1: response code = 200")
httpStatusCode, err = apiTest.AddProjectMember(*admin, projectID, *roles)
if err != nil {
t.Error("Error whihle add project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
}
//---------case 2: Response Code=409,User is ready in project.----------//
fmt.Println("case 2: Response Code=409,User is ready in project.")
httpStatusCode, err = apiTest.AddProjectMember(*admin, projectID, *roles)
if err != nil {
t.Error("Error while add project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(409), httpStatusCode, "Case 2: httpStatusCode should be 409")
}
//---------case 3: Response Code=404,User does not exist.----------//
fmt.Println("case 3: Response Code=404,User does not exist.")
errorRoles := &apilib.RoleParam{[]int32{1}, "T"}
httpStatusCode, err = apiTest.AddProjectMember(*admin, projectID, *errorRoles)
if err != nil {
t.Error("Error while add project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(404), httpStatusCode, "Case 3: httpStatusCode status should be 404")
}
/*
//---------case 4: Response Code=403,User in session does not have permission to the project..----------//
fmt.Println("case 4:User in session does not have permission to the project.")
httpStatusCode, err = apiTest.AddProjectMember(*testUser, projectID, *roles)
if err != nil {
t.Error("Error while add project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(403), httpStatusCode, "Case 3: httpStatusCode status should be 403")
}
*/
}
func TestGetMemByPIDUID(t *testing.T) {
var httpStatusCode int
var err error
assert := assert.New(t)
apiTest := newHarborAPI()
projectID := "1"
userID := strconv.Itoa(CommonGetUserID())
fmt.Println("Testing Member Get API by PID and UID")
//-------------------case 1 : response code = 200------------------------//
fmt.Println("case 1: response code = 200")
httpStatusCode, err = apiTest.GetMemByPIDUID(*admin, projectID, userID)
if err != nil {
t.Error("Error whihle get project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
}
}
func TestPutMem(t *testing.T) {
var httpStatusCode int
var err error
assert := assert.New(t)
apiTest := newHarborAPI()
projectID := "1"
userID := strconv.Itoa(CommonGetUserID())
roles := &apilib.RoleParam{[]int32{3}, TestUserName}
fmt.Println("Testing Member Put API")
//-------------------case 1 : response code = 200------------------------//
fmt.Println("case 1: response code = 200")
httpStatusCode, err = apiTest.PutProjectMember(*admin, projectID, userID, *roles)
if err != nil {
t.Error("Error whihle put project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
}
}
func TestDeleteMemUser(t *testing.T) {
var httpStatusCode int
var err error
assert := assert.New(t)
apiTest := newHarborAPI()
projectID := "1"
fmt.Println("Testing Member Delete API")
//-------------------case 1 : response code = 200------------------------//
fmt.Println("case 1: response code = 200")
id := strconv.Itoa(CommonGetUserID())
httpStatusCode, err = apiTest.DeleteProjectMember(*admin, projectID, id)
if err != nil {
t.Error("Error whihle add project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
}
CommonDelUser()
}

View File

@ -136,14 +136,19 @@ func TestListProjects(t *testing.T) {
//-------------------case 4 : add project member and check his role ------------------------//
CommonAddUser()
roles := &apilib.RoleParam{[]int32{2}, TestUserName}
member := &models.MemberReq{
Role: 2,
MemberUser: models.User{
Username: TestUserName,
},
}
projectID := strconv.Itoa(addPID)
httpStatusCode, err = apiTest.AddProjectMember(*admin, projectID, *roles)
httpStatusCode, err = apiTest.AddProjectMember(*admin, projectID, member)
if err != nil {
t.Error("Error whihle add project role member", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
assert.Equal(int(201), httpStatusCode, "httpStatusCode should be 201")
}
httpStatusCode, result, err = apiTest.ProjectsGet(
&apilib.ProjectQuery{

View File

@ -30,7 +30,7 @@ func TestProjectMemberAPI_Get(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/projectmembers",
url: "/api/projects/1/members",
},
code: http.StatusUnauthorized,
},
@ -38,7 +38,7 @@ func TestProjectMemberAPI_Get(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/projectmembers",
url: "/api/projects/1/members",
credential: admin,
},
code: http.StatusOK,
@ -47,7 +47,7 @@ func TestProjectMemberAPI_Get(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/0/projectmembers",
url: "/api/projects/0/members",
credential: admin,
},
code: http.StatusBadRequest,
@ -56,7 +56,7 @@ func TestProjectMemberAPI_Get(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/projectmembers/121",
url: "/api/projects/1/members/121",
credential: admin,
},
code: http.StatusNotFound,
@ -81,7 +81,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/projectmembers",
url: "/api/projects/1/members",
bodyJSON: &models.MemberReq{
Role: 1,
MemberUser: models.User{
@ -94,7 +94,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/projectmembers",
url: "/api/projects/1/members",
bodyJSON: &models.MemberReq{
Role: 1,
MemberUser: models.User{
@ -108,7 +108,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
&codeCheckingCase{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/projectmembers",
url: "/api/projects/1/members",
bodyJSON: &models.MemberReq{
Role: 1,
MemberUser: models.User{
@ -144,8 +144,8 @@ func TestProjectMemberAPI_PutAndDelete(t *testing.T) {
if err != nil {
t.Errorf("Error occurred when add project member: %v", err)
}
URL := fmt.Sprintf("/api/projects/1/projectmembers/%v", ID)
badURL := fmt.Sprintf("/api/projects/1/projectmembers/%v", 0)
URL := fmt.Sprintf("/api/projects/1/members/%v", ID)
badURL := fmt.Sprintf("/api/projects/1/members/%v", 0)
cases := []*codeCheckingCase{
// 401
&codeCheckingCase{

View File

@ -19,6 +19,8 @@ import (
"net/http/httptest"
"testing"
"github.com/vmware/harbor/src/common/dao/project"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vmware/harbor/src/common"
@ -314,17 +316,21 @@ func TestRepPolicyAPIList(t *testing.T) {
Password: "ProjectDev",
Email: "project_dev@test.com",
}
var proAdminPMID, proDevPMID int
proAdminID, err := dao.Register(projectAdmin)
if err != nil {
panic(err)
}
defer dao.DeleteUser(int(proAdminID))
if _, err = dao.AddProjectMember(1, int(proAdminID), models.PROJECTADMIN, common.UserMember); err != nil {
if proAdminPMID, err = project.AddProjectMember(models.Member{
ProjectID: 1,
Role: models.PROJECTADMIN,
EntityID: int(proAdminID),
EntityType: common.UserMember,
}); err != nil {
panic(err)
}
defer dao.DeleteProjectMember(1, int(proAdminID), common.UserMember)
defer project.DeleteProjectMemberByID(proAdminPMID)
proDevID, err := dao.Register(projectDev)
if err != nil {
@ -332,10 +338,15 @@ func TestRepPolicyAPIList(t *testing.T) {
}
defer dao.DeleteUser(int(proDevID))
if _, err = dao.AddProjectMember(1, int(proDevID), models.DEVELOPER, common.UserMember); err != nil {
if proDevPMID, err = project.AddProjectMember(models.Member{
ProjectID: 1,
Role: models.DEVELOPER,
EntityID: int(proDevID),
EntityType: common.UserMember,
}); err != nil {
panic(err)
}
defer dao.DeleteProjectMember(1, int(proDevID), common.UserMember)
defer project.DeleteProjectMemberByID(proDevPMID)
// 400: invalid project ID
runCodeCheckingCases(t, &codeCheckingCase{

View File

@ -20,6 +20,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/dao/project"
"github.com/vmware/harbor/src/common/models"
)
func TestGetRepos(t *testing.T) {
@ -226,6 +229,24 @@ func TestPopulateAuthor(t *testing.T) {
}
func TestPutOfRepository(t *testing.T) {
u, err := dao.GetUser(models.User{
Username: projAdmin.Name,
})
if err != nil {
t.Errorf("Error occurred when Register user: %v", err)
}
pmid, err := project.AddProjectMember(
models.Member{
ProjectID: 1,
Role: 1,
EntityID: int(u.UserID),
EntityType: "u"},
)
if err != nil {
t.Errorf("Error occurred when add project member: %v", err)
}
defer project.DeleteProjectMemberByID(pmid)
base := "/api/repositories/"
desc := struct {
Description string `json:"description"`
@ -307,7 +328,7 @@ func TestPutOfRepository(t *testing.T) {
// verify that the description is changed
repositories := []*repoResp{}
err := handleAndParse(&testingRequest{
err = handleAndParse(&testingRequest{
method: http.MethodGet,
url: base,
queryStruct: struct {

View File

@ -35,45 +35,6 @@ import (
uiutils "github.com/vmware/harbor/src/ui/utils"
)
//sysadmin has all privileges to all projects
func listRoles(userID int, projectID int64, entityType string) ([]models.Role, error) {
roles := make([]models.Role, 0, 1)
isSysAdmin, err := dao.IsAdminRole(userID)
if err != nil {
log.Errorf("failed to determine whether the user %d is system admin: %v", userID, err)
return roles, err
}
if isSysAdmin {
role, err := dao.GetRoleByID(models.PROJECTADMIN)
if err != nil {
log.Errorf("failed to get role %d: %v", models.PROJECTADMIN, err)
return roles, err
}
roles = append(roles, *role)
return roles, nil
}
rs, err := dao.GetUserProjectRoles(userID, projectID, entityType)
if err != nil {
log.Errorf("failed to get user %d 's roles for project %d: %v", userID, projectID, err)
return roles, err
}
roles = append(roles, rs...)
return roles, nil
}
func checkUserExists(name string) int {
u, err := dao.GetUser(models.User{Username: name})
if err != nil {
log.Errorf("Error occurred in GetUser, error: %v", err)
return 0
}
if u != nil {
return u.UserID
}
return 0
}
// SyncRegistry syncs the repositories of registry with database.
func SyncRegistry(pm promgr.ProjectManager) error {

View File

@ -46,6 +46,11 @@ func (d *Auth) SearchUser(username string) (*models.User, error) {
return dao.GetUser(queryCondition)
}
// OnBoardUser -
func (d *Auth) OnBoardUser(u *models.User) error {
return nil
}
func init() {
auth.Register("db_auth", &Auth{})
}

View File

@ -46,8 +46,7 @@ func initRouters() {
beego.Router("/sendEmail", &controllers.CommonController{}, "get:SendResetEmail")
//API:
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &api.ProjectUserMemberAPI{})
beego.Router("/api/projects/:pid([0-9]+)/projectmembers/?:pmid([0-9]+)", &api.ProjectMemberAPI{})
beego.Router("/api/projects/:pid([0-9]+)/members/?:pmid([0-9]+)", &api.ProjectMemberAPI{})
beego.Router("/api/projects/", &api.ProjectAPI{}, "head:Head")
beego.Router("/api/projects/:id([0-9]+)", &api.ProjectAPI{})

View File

@ -99,7 +99,7 @@ export class ConfirmationDialogComponent {
}
operate(): void {
if (!this.message){//Inproper condition
if (!this.message) {// Inproper condition
this.close();
return;
}
@ -120,7 +120,7 @@ export class ConfirmationDialogComponent {
}
confirm(): void {
if (!this.message){//Inproper condition
if (!this.message) {// Inproper condition
this.close();
return;
}

View File

@ -54,7 +54,7 @@ import {Observable} from "rxjs/Observable";
})
export class ListReplicationRuleComponent implements OnInit, OnChanges {
nullTime: string = '0001-01-01T00:00:00Z';
nullTime = '0001-01-01T00:00:00Z';
@Input() projectId: number;
@Input() isSystemAdmin: boolean;
@ -72,7 +72,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
@Output() openNewRule = new EventEmitter<any>();
@Output() replicateManual = new EventEmitter<ReplicationRule[]>();
projectScope: boolean = false;
projectScope = false;
rules: ReplicationRule[];
changedRules: ReplicationRule[];
@ -108,7 +108,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
}
ngOnInit(): void {
//Global scope
// Global scope
if (!this.projectScope) {
this.retrieveRules();
}
@ -120,15 +120,15 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
if (proIdChange.currentValue !== proIdChange.previousValue) {
if (proIdChange.currentValue) {
this.projectId = proIdChange.currentValue;
this.projectScope = true; //Scope is project, not global list
//Initially load the replication rule data
this.projectScope = true; // Scope is project, not global list
// Initially load the replication rule data
this.retrieveRules();
}
}
}
}
retrieveRules(ruleName: string = ''): void {
retrieveRules(ruleName = ''): void {
this.loading = true;
/*this.selectedRow = null;*/
toPromise<ReplicationRule[]>(this.replicationService
@ -184,7 +184,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
jobList(id: string | number): Promise<void> {
let ruleData: ReplicationJobItem[];
this.canDeleteRule = true;
let count: number = 0;
let count = 0;
return toPromise<ReplicationJob>(this.replicationService
.getJobs(id))
.then(response => {

View File

@ -58,7 +58,7 @@ describe('RecentLogComponent (inline template)', () => {
serviceConfig = TestBed.get(SERVICE_CONFIG);
logService = fixture.debugElement.injector.get(AccessLogService);
//Mock data
// Mock data
for (let i = 0; i < 18; i++) {
let item: AccessLogItem = {
log_id: 23 + i,
@ -80,7 +80,7 @@ describe('RecentLogComponent (inline template)', () => {
if (params && params.get('repository')) {
return Promise.resolve(mockData2);
} else {
if (params.get('page') == '1') {
if (params.get('page') === '1') {
mockData.data = mockItems.slice(0, 15);
} else {
mockData.data = mockItems.slice(15, 18)
@ -205,7 +205,7 @@ describe('RecentLogComponent (inline template)', () => {
fixture.whenStable().then(() => {
fixture.detectChanges();
let els: HTMLElement[] = fixture.nativeElement.querySelectorAll('.datagrid-row');
els = fixture.nativeElement.querySelectorAll('.datagrid-row');
expect(els).toBeTruthy();
expect(els.length).toEqual(16);
});

View File

@ -13,7 +13,7 @@ export abstract class SystemInfoService {
/**
* Get global system information.
* @abstract
* @returns
* @returns
*/
abstract getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo;
}
@ -23,14 +23,14 @@ export class SystemInfoDefaultService extends SystemInfoService {
constructor(
@Inject(SERVICE_CONFIG) private config: IServiceConfig,
private http: Http) {
super();
super();
}
getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo {
let url = this.config.systemInfoEndpoint ? this.config.systemInfoEndpoint : '/api/systeminfo';
return this.http.get(url, HTTP_GET_OPTIONS)
.toPromise()
.then(systemInfo=>systemInfo.json() as SystemInfo)
.catch(error=>Promise.reject(error));
.then(systemInfo => systemInfo.json() as SystemInfo)
.catch(error => Promise.reject(error));
}
}

View File

@ -8,7 +8,7 @@
<label for="member_name" class="col-md-4 form-group-label-override required">{{'MEMBER.NAME' | translate}}</label>
<label for="member_name" aria-haspopup="true" role="tooltip" [class.invalid]="!isMemberNameValid"
class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" (mouseleave)="leaveInput()">
<input type="text" id="member_name" [(ngModel)]="member.username"
<input type="text" id="member_name" [(ngModel)]="member.entity_name"
name="member_name"
size="20"
#memberName="ngModel"

View File

@ -108,7 +108,7 @@ export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
this.memberService
.listMembers(this.projectId, cont.value).toPromise()
.then((members: Member[]) => {
if (members.filter(m => { return m.username === cont.value }).length > 0) {
if (members.filter(m => { return m.entity_name === cont.value }).length > 0) {
this.isMemberNameValid = false;
this.memberTooltip = 'MEMBER.USERNAME_ALREADY_EXISTS';
}
@ -145,9 +145,9 @@ export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
}
onSubmit(): void {
if (!this.member.username || this.member.username.length === 0) { return; }
if (!this.member.entity_name || this.member.entity_name.length === 0) { return; }
this.memberService
.addMember(this.projectId, this.member.username, +this.member.role_id)
.addMember(this.projectId, this.member.entity_name, +this.member.role_id)
.subscribe(
response => {
this.messageHandlerService.showSuccess('MEMBER.ADDED_SUCCESS');
@ -185,7 +185,7 @@ export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
}
selectedName(username: string) {
this.member.username = username;
this.member.entity_name = username;
this.selectUserName = [];
}
@ -230,7 +230,7 @@ export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
this.addMemberOpened = true;
this.hasChanged = false;
this.member.role_id = 1;
this.member.username = '';
this.member.entity_name = '';
this.isMemberNameValid = true;
this.memberTooltip = 'MEMBER.USERNAME_IS_REQUIRED';
this.selectUserName = [];

View File

@ -32,10 +32,10 @@
<clr-dg-column>{{'MEMBER.NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'MEMBER.ROLE' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let m of members" [clrDgItem]="m">
<clr-dg-cell>{{m.username}}</clr-dg-cell>
<clr-dg-cell>{{m.entity_name}}</clr-dg-cell>
<clr-dg-cell>
<span *ngIf="ChangeRoleOngoing(m.username)" class="spinner spinner-inline"> Loading... </span>
<span *ngIf="!ChangeRoleOngoing(m.username)">{{roleInfo[m.role_id] | translate}}</span>
<span *ngIf="ChangeRoleOngoing(m.entity_name)" class="spinner spinner-inline"> Loading... </span>
<span *ngIf="!ChangeRoleOngoing(m.entity_name)">{{roleInfo[m.role_id] | translate}}</span>
</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>

View File

@ -135,7 +135,7 @@ export class MemberComponent implements OnInit, OnDestroy {
}
get onlySelf(): boolean {
if (this.selectedRow.length === 1 && this.selectedRow[0].user_id === this.currentUser.user_id) {
if (this.selectedRow.length === 1 && this.selectedRow[0].id === this.currentUser.user_id) {
return true;
}
return false;
@ -149,9 +149,9 @@ export class MemberComponent implements OnInit, OnDestroy {
let nameArr: string[] = [];
this.batchActionInfos = [];
m.forEach(data => {
nameArr.push(data.username);
nameArr.push(data.entity_name);
let initBatchMessage = new BatchInfo();
initBatchMessage.name = data.username;
initBatchMessage.name = data.entity_name;
this.batchActionInfos.push(initBatchMessage);
});
@ -163,14 +163,14 @@ export class MemberComponent implements OnInit, OnDestroy {
if (members && members.length) {
let promiseList: any[] = [];
members.forEach(member => {
if (member.user_id === this.currentUser.user_id) {
let foundMember = this.batchActionInfos.find(batchInfo => batchInfo.name === member.username);
if (member.id === this.currentUser.user_id) {
let foundMember = this.batchActionInfos.find(batchInfo => batchInfo.name === member.entity_name);
this.translate.get("BATCH.SWITCH_FAILURE").subscribe(res => {
this.messageHandlerService.handleError(res + ": " + foundMember.name);
foundMember = BathInfoChanges(foundMember, res, false, true);
});
} else {
promiseList.push(this.changeOperate(this.projectId, member.user_id, this.roleNum, member.username));
promiseList.push(this.changeOperate(this.projectId, member.id, this.roleNum, member.entity_name));
}
});
@ -216,9 +216,9 @@ export class MemberComponent implements OnInit, OnDestroy {
this.batchDeletionInfos = [];
if (m && m.length) {
m.forEach(data => {
nameArr.push(data.username);
nameArr.push(data.entity_name);
let initBatchMessage = new BatchInfo ();
initBatchMessage.name = data.username;
initBatchMessage.name = data.entity_name;
this.batchDeletionInfos.push(initBatchMessage);
});
this.OperateDialogService.addBatchInfoList(this.batchDeletionInfos);
@ -239,13 +239,13 @@ export class MemberComponent implements OnInit, OnDestroy {
if (members && members.length) {
let promiseLists: any[] = [];
members.forEach(member => {
if (member.user_id === this.currentUser.user_id) {
let findedList = this.batchDeletionInfos.find(data => data.name === member.username);
if (member.id === this.currentUser.user_id) {
let findedList = this.batchDeletionInfos.find(data => data.name === member.entity_name);
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});
}else {
promiseLists.push(this.delOperate(this.projectId, member.user_id, member.username));
promiseLists.push(this.delOperate(this.projectId, member.id, member.entity_name));
}
});

View File

@ -24,34 +24,34 @@ import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "../../shared/shared.utils";
@Injectable()
export class MemberService {
constructor(private http: Http) {}
listMembers(projectId: number, username: string): Observable<Member[]> {
return this.http
.get(`/api/projects/${projectId}/members?username=${username}`, HTTP_GET_OPTIONS)
.map(response=>response.json() as Member[])
.catch(error=>Observable.throw(error));
.get(`/api/projects/${projectId}/members`, HTTP_GET_OPTIONS)
.map(response => response.json() as Member[])
.catch(error => Observable.throw(error));
}
addMember(projectId: number, username: string, roleId: number): Observable<any> {
return this.http
.post(`/api/projects/${projectId}/members`, { username: username, roles: [ roleId ] }, HTTP_JSON_OPTIONS)
.map(response=>response.status)
.catch(error=>Observable.throw(error));
.post(`/api/projects/${projectId}/members`, { role_id: roleId, member_user: {username: username} }, HTTP_JSON_OPTIONS)
.map(response => response.status)
.catch(error => Observable.throw(error));
}
changeMemberRole(projectId: number, userId: number, roleId: number): Promise<any> {
return this.http
.put(`/api/projects/${projectId}/members/${userId}`, { roles: [ roleId ]}, HTTP_JSON_OPTIONS).toPromise()
.then(response=>response.status)
.catch(error=>Promise.reject(error));
.put(`/api/projects/${projectId}/members/${userId}`, { role_id: roleId }, HTTP_JSON_OPTIONS).toPromise()
.then(response => response.status)
.catch(error => Promise.reject(error));
}
deleteMember(projectId: number, userId: number): Promise<any> {
return this.http
.delete(`/api/projects/${projectId}/members/${userId}`).toPromise()
.then(response=>response.status)
.catch(error=>Promise.reject(error));
.then(response => response.status)
.catch(error => Promise.reject(error));
}
}

View File

@ -30,9 +30,11 @@
*/
export class Member {
user_id: number;
username: string;
id: number;
project_id: number;
entity_name: string;
role_name: string;
has_admin_role: number;
role_id: number;
entity_id: number;
entity_type: string;
}

View File

@ -42,7 +42,7 @@ export class ProjectRoutingResolver implements Resolve<Project>{
if (currentUser) {
let projectMembers = this.sessionService.getProjectMembers();
if (projectMembers) {
let currentMember = projectMembers.find(m => m.user_id === currentUser.user_id);
let currentMember = projectMembers.find(m => m.entity_id === currentUser.user_id);
if (currentMember) {
project.is_member = true;
project.has_project_admin_role = (currentMember.role_name === 'projectAdmin');

View File

@ -47,32 +47,32 @@ export class TargetExistsValidatorDirective implements Validator, OnChanges {
}
validate(control: AbstractControl): {[key: string]: any} {
return this.valFn(control);
}
}
targetExistsValidator(target: string): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
switch(target) {
switch (target) {
case 'PROJECT_NAME':
return new Promise(resolve=>{
return new Promise(resolve => {
this.projectService
.checkProjectExists(control.value)
.subscribe(res=>resolve({'targetExists': true}),error=>resolve(null));
.subscribe(res => resolve({'targetExists': true}),error=>resolve(null));
});
case 'MEMBER_NAME':
return new Promise(resolve=>{
return new Promise(resolve => {
this.memberService
.listMembers(this.projectId, control.value)
.subscribe((members: Member[])=>{
return members.filter(m=>{
if(m.username === control.value) {
.subscribe((members: Member[]) => {
return members.filter(m => {
if (m.entity_name === control.value) {
return true;
}
return null;
}).length > 0 ?
resolve({'targetExists': true}) : resolve(null);
},error=>resolve(null));
resolve({'targetExists': true}) : resolve(null);
}, error => resolve(null));
});
}
}
};
}
}