mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +01:00
Merge pull request #14577 from stonezdj/21mar30_projectmember
Refactor project member api to new programming model
This commit is contained in:
commit
81a72c0435
@ -176,170 +176,6 @@ paths:
|
|||||||
description: Project or metadata does not exist.
|
description: Project or metadata does not exist.
|
||||||
'500':
|
'500':
|
||||||
description: Internal server errors.
|
description: Internal server errors.
|
||||||
'/projects/{project_id}/members':
|
|
||||||
get:
|
|
||||||
summary: Get all project member information
|
|
||||||
description: Get all project member information
|
|
||||||
parameters:
|
|
||||||
- name: project_id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Relevant project ID.
|
|
||||||
- name: entityname
|
|
||||||
in: query
|
|
||||||
type: string
|
|
||||||
description: The entity name to search.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Get project members successfully.
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/ProjectMemberEntity'
|
|
||||||
'400':
|
|
||||||
description: The project id is invalid.
|
|
||||||
'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: Create project member
|
|
||||||
description: 'Create project member relationship, the member can be one of the user_member and group_member, The user_member need to specify user_id or username. If the user already exist in harbor DB, specify the user_id, If does not exist in harbor DB, it will SearchAndOnBoard the user. The group_member need to specify id or ldap_group_dn. If the group already exist in harbor DB. specify the user group''s id, If does not exist, it will SearchAndOnBoard the group. '
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
parameters:
|
|
||||||
- name: project_id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Relevant project ID.
|
|
||||||
- name: project_member
|
|
||||||
in: body
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/ProjectMember'
|
|
||||||
responses:
|
|
||||||
'201':
|
|
||||||
description: Project member created successfully.
|
|
||||||
headers:
|
|
||||||
Location:
|
|
||||||
type: string
|
|
||||||
description: The URL of the created resource
|
|
||||||
'400':
|
|
||||||
description: 'Illegal format of project member or project id is invalid, or LDAP DN is invalid.'
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User in session does not have permission to the project.
|
|
||||||
'409':
|
|
||||||
description: A user group with same group name already exist or an LDAP user group with same DN already exist.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
'/projects/{project_id}/members/{mid}':
|
|
||||||
get:
|
|
||||||
summary: Get the project member information
|
|
||||||
description: Get the project member information
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
parameters:
|
|
||||||
- name: project_id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Relevant project ID.
|
|
||||||
- name: mid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: The member ID
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Project member retrieved successfully.
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/ProjectMemberEntity'
|
|
||||||
'400':
|
|
||||||
description: 'Illegal format of project member or invalid project id, member id.'
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User in session does not have permission to the project.
|
|
||||||
'404':
|
|
||||||
description: Project or projet member does not exist.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
put:
|
|
||||||
summary: Update project member
|
|
||||||
description: Update project member relationship
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
parameters:
|
|
||||||
- name: project_id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Relevant project ID.
|
|
||||||
- name: mid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Member ID.
|
|
||||||
- name: role
|
|
||||||
in: body
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RoleRequest'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Project member updated successfully.
|
|
||||||
'400':
|
|
||||||
description: 'Invalid role id, it should be 1,2 or 3, or invalid project id, or invalid member id.'
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User in session does not have permission to the project.
|
|
||||||
'404':
|
|
||||||
description: project or project member does not exist.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
delete:
|
|
||||||
summary: Delete project member
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
parameters:
|
|
||||||
- name: project_id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Relevant project ID.
|
|
||||||
- name: mid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Member ID.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Project member deleted successfully.
|
|
||||||
'400':
|
|
||||||
description: The project id or project member id is invalid.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User in session does not have permission to the project.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
/statistics:
|
/statistics:
|
||||||
get:
|
get:
|
||||||
summary: Get projects number and repositories number relevant to the user
|
summary: Get projects number and repositories number relevant to the user
|
||||||
|
@ -447,6 +447,170 @@ paths:
|
|||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
|
'/projects/{project_name_or_id}/members':
|
||||||
|
get:
|
||||||
|
summary: Get all project member information
|
||||||
|
description: Get all project member information
|
||||||
|
operationId: listProjectMembers
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/isResourceName'
|
||||||
|
- $ref: '#/parameters/projectNameOrId'
|
||||||
|
- $ref: '#/parameters/page'
|
||||||
|
- $ref: '#/parameters/pageSize'
|
||||||
|
- name: entityname
|
||||||
|
in: query
|
||||||
|
type: string
|
||||||
|
description: The entity name to search.
|
||||||
|
tags:
|
||||||
|
- member
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Get project members successfully.
|
||||||
|
headers:
|
||||||
|
X-Total-Count:
|
||||||
|
description: The total count of members
|
||||||
|
type: integer
|
||||||
|
Link:
|
||||||
|
description: Link refers to the previous page and next page
|
||||||
|
type: string
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/ProjectMemberEntity'
|
||||||
|
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'404':
|
||||||
|
$ref: '#/responses/404'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
post:
|
||||||
|
summary: Create project member
|
||||||
|
operationId: createProjectMember
|
||||||
|
description: 'Create project member relationship, the member can be one of the user_member and group_member, The user_member need to specify user_id or username. If the user already exist in harbor DB, specify the user_id, If does not exist in harbor DB, it will SearchAndOnBoard the user. The group_member need to specify id or ldap_group_dn. If the group already exist in harbor DB. specify the user group''s id, If does not exist, it will SearchAndOnBoard the group. '
|
||||||
|
tags:
|
||||||
|
- member
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/isResourceName'
|
||||||
|
- $ref: '#/parameters/projectNameOrId'
|
||||||
|
- name: project_member
|
||||||
|
in: body
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ProjectMember'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Project member created successfully.
|
||||||
|
headers:
|
||||||
|
Location:
|
||||||
|
type: string
|
||||||
|
description: The URL of the created resource
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'409':
|
||||||
|
$ref: '#/responses/409'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
'/projects/{project_name_or_id}/members/{mid}':
|
||||||
|
get:
|
||||||
|
summary: Get the project member information
|
||||||
|
description: Get the project member information
|
||||||
|
operationId: getProjectMember
|
||||||
|
tags:
|
||||||
|
- member
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/isResourceName'
|
||||||
|
- $ref: '#/parameters/projectNameOrId'
|
||||||
|
- name: mid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: The member ID
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Project member retrieved successfully.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ProjectMemberEntity'
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'404':
|
||||||
|
$ref: '#/responses/404'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
put:
|
||||||
|
summary: Update project member
|
||||||
|
description: Update project member relationship
|
||||||
|
operationId: updateProjectMember
|
||||||
|
tags:
|
||||||
|
- member
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/isResourceName'
|
||||||
|
- $ref: '#/parameters/projectNameOrId'
|
||||||
|
- name: mid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Member ID.
|
||||||
|
- name: role
|
||||||
|
in: body
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RoleRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '#/responses/200'
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'404':
|
||||||
|
$ref: '#/responses/404'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
delete:
|
||||||
|
summary: Delete project member
|
||||||
|
operationId: deleteProjectMember
|
||||||
|
tags:
|
||||||
|
- member
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/isResourceName'
|
||||||
|
- $ref: '#/parameters/projectNameOrId'
|
||||||
|
- name: mid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Member ID.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '#/responses/200'
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
/repositories:
|
/repositories:
|
||||||
get:
|
get:
|
||||||
summary: List all authorized repositories
|
summary: List all authorized repositories
|
||||||
@ -4783,6 +4947,7 @@ parameters:
|
|||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Success
|
description: Success
|
||||||
@ -7392,6 +7557,55 @@ definitions:
|
|||||||
editable:
|
editable:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: The configure item can be updated or not
|
description: The configure item can be updated or not
|
||||||
|
ProjectMemberEntity:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
description: the project member id
|
||||||
|
project_id:
|
||||||
|
type: integer
|
||||||
|
description: the project id
|
||||||
|
entity_name:
|
||||||
|
type: string
|
||||||
|
description: the name of the group member.
|
||||||
|
role_name:
|
||||||
|
type: string
|
||||||
|
description: the name of the role
|
||||||
|
role_id:
|
||||||
|
type: integer
|
||||||
|
description: the role id
|
||||||
|
entity_id:
|
||||||
|
type: integer
|
||||||
|
description: 'the id of entity, if the member is a user, it is user_id in user table. if the member is a user group, it is the user group''s ID in user_group table.'
|
||||||
|
entity_type:
|
||||||
|
type: string
|
||||||
|
description: 'the entity''s type, u for user entity, g for group entity.'
|
||||||
|
ProjectMember:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
role_id:
|
||||||
|
type: integer
|
||||||
|
description: 'The role id 1 for projectAdmin, 2 for developer, 3 for guest, 4 for maintainer'
|
||||||
|
member_user:
|
||||||
|
$ref: '#/definitions/UserEntity'
|
||||||
|
member_group:
|
||||||
|
$ref: '#/definitions/UserGroup'
|
||||||
|
RoleRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
role_id:
|
||||||
|
type: integer
|
||||||
|
description: 'The role id 1 for projectAdmin, 2 for developer, 3 for guest, 4 for maintainer'
|
||||||
|
UserEntity:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
user_id:
|
||||||
|
type: integer
|
||||||
|
description: The ID of the user.
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
description: The name of the user.
|
||||||
UserProfile:
|
UserProfile:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -1,403 +0,0 @@
|
|||||||
// Copyright Project Harbor Authors
|
|
||||||
//
|
|
||||||
// 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 project
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup/model"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/db"
|
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
|
|
||||||
// databases := []string{"mysql", "sqlite"}
|
|
||||||
databases := []string{"postgresql"}
|
|
||||||
for _, database := range databases {
|
|
||||||
log.Infof("run test cases for database: %s", database)
|
|
||||||
|
|
||||||
result := 1
|
|
||||||
switch database {
|
|
||||||
case "postgresql":
|
|
||||||
dao.PrepareTestForPostgresSQL()
|
|
||||||
default:
|
|
||||||
log.Fatalf("invalid database: %s", database)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract to test utils
|
|
||||||
initSqls := []string{
|
|
||||||
"insert into harbor_user (username, email, password, realname) values ('member_test_01', 'member_test_01@example.com', '123456', 'member_test_01')",
|
|
||||||
"insert into project (name, owner_id) values ('member_test_01', 1)",
|
|
||||||
"insert into user_group (group_name, group_type, ldap_group_dn) values ('test_group_01', 1, 'CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com')",
|
|
||||||
"update project set owner_id = (select user_id from harbor_user where username = 'member_test_01') where name = 'member_test_01'",
|
|
||||||
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_01') , (select user_id from harbor_user where username = 'member_test_01'), 'u', 1)",
|
|
||||||
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_01') , (select id from user_group where group_name = 'test_group_01'), 'g', 1)",
|
|
||||||
|
|
||||||
"insert into harbor_user (username, email, password, realname) values ('member_test_02', 'member_test_02@example.com', '123456', 'member_test_02')",
|
|
||||||
"insert into project (name, owner_id) values ('member_test_02', 1)",
|
|
||||||
"insert into user_group (group_name, group_type, ldap_group_dn) values ('test_group_02', 1, 'CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com')",
|
|
||||||
"update project set owner_id = (select user_id from harbor_user where username = 'member_test_02') where name = 'member_test_02'",
|
|
||||||
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_02') , (select user_id from harbor_user where username = 'member_test_02'), 'u', 1)",
|
|
||||||
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_02') , (select id from user_group where group_name = 'test_group_02'), 'g', 1)",
|
|
||||||
}
|
|
||||||
|
|
||||||
clearSqls := []string{
|
|
||||||
"delete from project where name='member_test_01' or name='member_test_02'",
|
|
||||||
"delete from harbor_user where username='member_test_01' or username='member_test_02' or username='pm_sample'",
|
|
||||||
"delete from user_group",
|
|
||||||
"delete from project_member where id > 1",
|
|
||||||
}
|
|
||||||
dao.PrepareTestData(clearSqls, initSqls)
|
|
||||||
config.Init()
|
|
||||||
result = m.Run()
|
|
||||||
|
|
||||||
if result != 0 {
|
|
||||||
os.Exit(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteProjectMemberByID(t *testing.T) {
|
|
||||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
|
||||||
|
|
||||||
if currentProject == nil || err != nil {
|
|
||||||
fmt.Println("Failed to load project!")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Load project %+v", currentProject)
|
|
||||||
}
|
|
||||||
var addMember = models.Member{
|
|
||||||
ProjectID: currentProject.ProjectID,
|
|
||||||
EntityID: 1,
|
|
||||||
EntityType: common.UserMember,
|
|
||||||
Role: common.RoleDeveloper,
|
|
||||||
}
|
|
||||||
|
|
||||||
pmid, err := AddProjectMember(addMember)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to add project member error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
pmid int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"Delete created", args{pmid}, false},
|
|
||||||
{"Delete non exist", args{-13}, false},
|
|
||||||
{"Delete non exist", args{13}, false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if err := DeleteProjectMemberByID(tt.args.pmid); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("DeleteProjectMemberByID() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestAddProjectMember(t *testing.T) {
|
|
||||||
|
|
||||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
|
||||||
}
|
|
||||||
member := models.Member{
|
|
||||||
ProjectID: currentProject.ProjectID,
|
|
||||||
EntityID: 1,
|
|
||||||
EntityType: common.UserMember,
|
|
||||||
Role: common.RoleProjectAdmin,
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Current project id %v", currentProject.ProjectID)
|
|
||||||
pmid, err := AddProjectMember(member)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred in AddProjectMember: %v", err)
|
|
||||||
}
|
|
||||||
if pmid == 0 {
|
|
||||||
t.Errorf("Error add project member, pmid=0")
|
|
||||||
}
|
|
||||||
|
|
||||||
queryMember := models.Member{
|
|
||||||
ProjectID: currentProject.ProjectID,
|
|
||||||
ID: pmid,
|
|
||||||
}
|
|
||||||
|
|
||||||
memberList, err := GetProjectMember(queryMember)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to query project member, %v, error: %v", queryMember, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(memberList) == 0 {
|
|
||||||
t.Errorf("Failed to query project member, %v", queryMember)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = AddProjectMember(models.Member{
|
|
||||||
ProjectID: -1,
|
|
||||||
EntityID: 1,
|
|
||||||
EntityType: common.UserMember,
|
|
||||||
Role: common.RoleProjectAdmin,
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should failed with negative projectID")
|
|
||||||
}
|
|
||||||
_, err = AddProjectMember(models.Member{
|
|
||||||
ProjectID: 1,
|
|
||||||
EntityID: -1,
|
|
||||||
EntityType: common.UserMember,
|
|
||||||
Role: common.RoleProjectAdmin,
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should failed with negative entityID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestUpdateProjectMemberRole(t *testing.T) {
|
|
||||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
|
||||||
user := models.User{
|
|
||||||
Username: "pm_sample",
|
|
||||||
Email: "pm_sample@example.com",
|
|
||||||
Realname: "pm_sample",
|
|
||||||
Password: "1234567d",
|
|
||||||
}
|
|
||||||
o := dao.GetOrmer()
|
|
||||||
userID, err := o.Insert(&user)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when add user: %v", err)
|
|
||||||
}
|
|
||||||
member := models.Member{
|
|
||||||
ProjectID: currentProject.ProjectID,
|
|
||||||
EntityID: int(userID),
|
|
||||||
EntityType: common.UserMember,
|
|
||||||
Role: common.RoleProjectAdmin,
|
|
||||||
}
|
|
||||||
|
|
||||||
pmid, err := AddProjectMember(member)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred in UpdateProjectMember: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateProjectMemberRole(pmid, common.RoleDeveloper)
|
|
||||||
|
|
||||||
queryMember := models.Member{
|
|
||||||
ProjectID: currentProject.ProjectID,
|
|
||||||
EntityID: int(userID),
|
|
||||||
EntityType: common.UserMember,
|
|
||||||
}
|
|
||||||
|
|
||||||
memberList, err := GetProjectMember(queryMember)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred in GetProjectMember: %v", err)
|
|
||||||
}
|
|
||||||
if len(memberList) != 1 {
|
|
||||||
t.Errorf("Error occurred in Failed, size: %d, condition:%+v", len(memberList), queryMember)
|
|
||||||
}
|
|
||||||
memberItem := memberList[0]
|
|
||||||
if memberItem.Role != common.RoleDeveloper || memberItem.Entityname != user.Username {
|
|
||||||
t.Errorf("member doesn't match!")
|
|
||||||
}
|
|
||||||
|
|
||||||
memberList2, err := SearchMemberByName(currentProject.ProjectID, "pm_sample")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when SearchMemberByName: %v", err)
|
|
||||||
}
|
|
||||||
if len(memberList2) == 0 {
|
|
||||||
t.Errorf("Failed to search user pm_sample, project_id:%v, entityname:%v",
|
|
||||||
currentProject.ProjectID, "pm_sample")
|
|
||||||
}
|
|
||||||
|
|
||||||
memberList3, err := SearchMemberByName(currentProject.ProjectID, "")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when SearchMemberByName: %v", err)
|
|
||||||
}
|
|
||||||
if len(memberList3) == 0 {
|
|
||||||
t.Errorf("Failed to search user pm_sample, project_id:%v, entityname is empty",
|
|
||||||
currentProject.ProjectID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetProjectMember(t *testing.T) {
|
|
||||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
|
||||||
}
|
|
||||||
var memberList1 = []*models.Member{
|
|
||||||
{
|
|
||||||
ID: 346,
|
|
||||||
Entityname: "admin",
|
|
||||||
Rolename: "projectAdmin",
|
|
||||||
Role: 1,
|
|
||||||
EntityID: 1,
|
|
||||||
EntityType: "u"},
|
|
||||||
}
|
|
||||||
var memberList2 = []*models.Member{
|
|
||||||
{
|
|
||||||
ID: 398,
|
|
||||||
Entityname: "test_group_01",
|
|
||||||
Rolename: "projectAdmin",
|
|
||||||
Role: 1,
|
|
||||||
EntityType: "g"},
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
queryMember models.Member
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []*models.Member
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"Query default project member", args{models.Member{ProjectID: currentProject.ProjectID, Entityname: "admin"}}, memberList1, false},
|
|
||||||
{"Query default project member group", args{models.Member{ProjectID: currentProject.ProjectID, Entityname: "test_group_01"}}, memberList2, false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := GetProjectMember(tt.args.queryMember)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("GetProjectMember() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(got) != 1 {
|
|
||||||
t.Errorf("Error occurred when query project member")
|
|
||||||
}
|
|
||||||
itemGot := got[0]
|
|
||||||
itemWant := tt.want[0]
|
|
||||||
|
|
||||||
if itemGot.Entityname != itemWant.Entityname || itemGot.Role != itemWant.Role || itemGot.EntityType != itemWant.EntityType {
|
|
||||||
t.Errorf("test failed, got:%+v, want:%+v", itemGot, itemWant)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetTotalOfProjectMembers(t *testing.T) {
|
|
||||||
currentProject, _ := dao.GetProjectByName("member_test_02")
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
projectID int64
|
|
||||||
roles []int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want int64
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"Get total of project admin", args{currentProject.ProjectID, []int{common.RoleProjectAdmin}}, 2, false},
|
|
||||||
{"Get total of maintainer", args{currentProject.ProjectID, []int{common.RoleMaintainer}}, 0, false},
|
|
||||||
{"Get total of developer", args{currentProject.ProjectID, []int{common.RoleDeveloper}}, 0, false},
|
|
||||||
{"Get total of guest", args{currentProject.ProjectID, []int{common.RoleGuest}}, 0, false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := GetTotalOfProjectMembers(tt.args.projectID, tt.args.roles...)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("GetTotalOfProjectMembers() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("GetTotalOfProjectMembers() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListRoles(t *testing.T) {
|
|
||||||
// nil user
|
|
||||||
roles, err := ListRoles(nil, 1)
|
|
||||||
require.Nil(t, err)
|
|
||||||
assert.Len(t, roles, 0)
|
|
||||||
|
|
||||||
user, err := dao.GetUser(models.User{Username: "member_test_01"})
|
|
||||||
require.Nil(t, err)
|
|
||||||
project, err := dao.GetProjectByName("member_test_01")
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
// user with empty groups
|
|
||||||
roles, err = ListRoles(user, project.ProjectID)
|
|
||||||
require.Nil(t, err)
|
|
||||||
assert.Len(t, roles, 1)
|
|
||||||
|
|
||||||
// user with a group whose ID doesn't exist
|
|
||||||
user.GroupIDs = []int{9999}
|
|
||||||
roles, err = ListRoles(user, project.ProjectID)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Len(t, roles, 1)
|
|
||||||
assert.Equal(t, common.RoleProjectAdmin, roles[0])
|
|
||||||
ctx := orm.Context()
|
|
||||||
// user with a valid group
|
|
||||||
groupID, err := usergroup.Mgr.Create(ctx, model.UserGroup{
|
|
||||||
GroupName: "group_for_list_role",
|
|
||||||
GroupType: 1,
|
|
||||||
LdapGroupDN: "CN=list_role_users,OU=sample,OU=vmware,DC=harbor,DC=com",
|
|
||||||
})
|
|
||||||
require.Nil(t, err)
|
|
||||||
defer usergroup.Mgr.Delete(orm.Context(), groupID)
|
|
||||||
|
|
||||||
memberID, err := AddProjectMember(models.Member{
|
|
||||||
ProjectID: project.ProjectID,
|
|
||||||
Role: common.RoleDeveloper,
|
|
||||||
EntityID: groupID,
|
|
||||||
EntityType: "g",
|
|
||||||
})
|
|
||||||
require.Nil(t, err)
|
|
||||||
defer DeleteProjectMemberByID(memberID)
|
|
||||||
|
|
||||||
user.GroupIDs = []int{groupID}
|
|
||||||
roles, err = ListRoles(user, project.ProjectID)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Len(t, roles, 2)
|
|
||||||
assert.Equal(t, common.RoleProjectAdmin, roles[0])
|
|
||||||
assert.Equal(t, common.RoleDeveloper, roles[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrepareGroupTest() {
|
|
||||||
initSqls := []string{
|
|
||||||
`insert into user_group (group_name, group_type, ldap_group_dn) values ('harbor_group_01', 1, 'cn=harbor_user,dc=example,dc=com')`,
|
|
||||||
`insert into harbor_user (username, email, password, realname) values ('sample01', 'sample01@example.com', 'harbor12345', 'sample01')`,
|
|
||||||
`insert into project (name, owner_id) values ('group_project', 1)`,
|
|
||||||
`insert into project (name, owner_id) values ('group_project_private', 1)`,
|
|
||||||
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project'), 'public', 'false')`,
|
|
||||||
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project_private'), 'public', 'false')`,
|
|
||||||
`insert into project_member (project_id, entity_id, entity_type, role) values ((select project_id from project where name = 'group_project'), (select id from user_group where group_name = 'harbor_group_01'),'g', 2)`,
|
|
||||||
}
|
|
||||||
|
|
||||||
clearSqls := []string{
|
|
||||||
`delete from project_metadata where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
|
|
||||||
`delete from project where name in ('group_project', 'group_project_private')`,
|
|
||||||
`delete from project_member where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
|
|
||||||
`delete from user_group where group_name = 'harbor_group_01'`,
|
|
||||||
`delete from harbor_user where username = 'sample01'`,
|
|
||||||
}
|
|
||||||
dao.PrepareTestData(clearSqls, initSqls)
|
|
||||||
}
|
|
234
src/controller/member/controller.go
Normal file
234
src/controller/member/controller.go
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// 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 member
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/core/auth"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/usergroup"
|
||||||
|
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Controller defines the operation related to project member
|
||||||
|
type Controller interface {
|
||||||
|
// Get get the project member with ID
|
||||||
|
Get(ctx context.Context, projectNameOrID interface{}, memberID int) (*models.Member, error)
|
||||||
|
// Create add project member to project
|
||||||
|
Create(ctx context.Context, projectNameOrID interface{}, req Request) (int, error)
|
||||||
|
// Delete member from project
|
||||||
|
Delete(ctx context.Context, projectNameOrID interface{}, memberID int) error
|
||||||
|
// List list all project members with condition
|
||||||
|
List(ctx context.Context, projectNameOrID interface{}, entityName string, query *q.Query) ([]*models.Member, error)
|
||||||
|
// UpdateRole update the project member role
|
||||||
|
UpdateRole(ctx context.Context, projectNameOrID interface{}, memberID int, role int) error
|
||||||
|
// Count get the total amount of project members
|
||||||
|
Count(ctx context.Context, projectNameOrID interface{}, query *q.Query) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request - Project Member Request
|
||||||
|
type Request struct {
|
||||||
|
ProjectID int64 `json:"project_id"`
|
||||||
|
Role int `json:"role_id,omitempty"`
|
||||||
|
MemberUser User `json:"member_user,omitempty"`
|
||||||
|
MemberGroup UserGroup `json:"member_group,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User ...
|
||||||
|
type User struct {
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserGroup ...
|
||||||
|
type UserGroup struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
GroupName string `json:"group_name,omitempty"`
|
||||||
|
GroupType int `json:"group_type,omitempty"`
|
||||||
|
LdapGroupDN string `json:"ldap_group_dn,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDuplicateProjectMember ...
|
||||||
|
var ErrDuplicateProjectMember = errors.New("The project member specified already exist")
|
||||||
|
|
||||||
|
// ErrInvalidRole ...
|
||||||
|
var ErrInvalidRole = errors.New("Failed to update project member, role is not in 1,2,3")
|
||||||
|
|
||||||
|
type controller struct {
|
||||||
|
userManager user.Manager
|
||||||
|
mgr member.Manager
|
||||||
|
projectMgr project.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewController ...
|
||||||
|
func NewController() Controller {
|
||||||
|
return &controller{mgr: member.Mgr, projectMgr: project.Mgr, userManager: user.New()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) Count(ctx context.Context, projectNameOrID interface{}, query *q.Query) (int, error) {
|
||||||
|
p, err := c.projectMgr.Get(ctx, projectNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return c.mgr.GetTotalOfProjectMembers(ctx, p.ProjectID, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) UpdateRole(ctx context.Context, projectNameOrID interface{}, memberID int, role int) error {
|
||||||
|
p, err := c.projectMgr.Get(ctx, projectNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return errors.BadRequestError(nil).WithMessage("project is not found")
|
||||||
|
}
|
||||||
|
return c.mgr.UpdateRole(ctx, p.ProjectID, memberID, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) Get(ctx context.Context, projectNameOrID interface{}, memberID int) (*models.Member, error) {
|
||||||
|
p, err := c.projectMgr.Get(ctx, projectNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return nil, errors.BadRequestError(nil).WithMessage("project is not found")
|
||||||
|
}
|
||||||
|
return c.mgr.Get(ctx, p.ProjectID, memberID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) Create(ctx context.Context, projectNameOrID interface{}, req Request) (int, error) {
|
||||||
|
p, err := c.projectMgr.Get(ctx, projectNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return 0, errors.BadRequestError(nil).WithMessage("project is not found")
|
||||||
|
}
|
||||||
|
var member models.Member
|
||||||
|
member.ProjectID = p.ProjectID
|
||||||
|
member.Role = req.Role
|
||||||
|
member.EntityType = common.GroupMember
|
||||||
|
|
||||||
|
if req.MemberUser.UserID > 0 {
|
||||||
|
member.EntityID = req.MemberUser.UserID
|
||||||
|
member.EntityType = common.UserMember
|
||||||
|
} else if req.MemberGroup.ID > 0 {
|
||||||
|
member.EntityID = req.MemberGroup.ID
|
||||||
|
} else if len(req.MemberUser.Username) > 0 {
|
||||||
|
// If username is provided, search userid by username
|
||||||
|
var userID int
|
||||||
|
member.EntityType = common.UserMember
|
||||||
|
u, err := c.userManager.GetByName(ctx, req.MemberUser.Username)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if u != nil {
|
||||||
|
userID = u.UserID
|
||||||
|
} else {
|
||||||
|
userID, err = auth.SearchAndOnBoardUser(req.MemberUser.Username)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
member.EntityID = userID
|
||||||
|
} else if len(req.MemberGroup.LdapGroupDN) > 0 {
|
||||||
|
req.MemberGroup.GroupType = common.LDAPGroupType
|
||||||
|
// If groupname provided, use the provided groupname to name this group
|
||||||
|
groupID, err := auth.SearchAndOnBoardGroup(req.MemberGroup.LdapGroupDN, req.MemberGroup.GroupName)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
member.EntityID = groupID
|
||||||
|
} else if len(req.MemberGroup.GroupName) > 0 && req.MemberGroup.GroupType == common.HTTPGroupType || req.MemberGroup.GroupType == common.OIDCGroupType {
|
||||||
|
ugs, err := usergroup.Mgr.List(ctx, ugModel.UserGroup{GroupName: req.MemberGroup.GroupName, GroupType: req.MemberGroup.GroupType})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(ugs) == 0 {
|
||||||
|
groupID, err := auth.SearchAndOnBoardGroup(req.MemberGroup.GroupName, "")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
member.EntityID = groupID
|
||||||
|
} else {
|
||||||
|
member.EntityID = ugs[0].ID
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if member.EntityID <= 0 {
|
||||||
|
return 0, fmt.Errorf("Can not get valid member entity, request: %+v", req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if member already exist in current project
|
||||||
|
memberList, err := c.mgr.List(ctx, models.Member{
|
||||||
|
ProjectID: member.ProjectID,
|
||||||
|
EntityID: member.EntityID,
|
||||||
|
EntityType: member.EntityType,
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(memberList) > 0 {
|
||||||
|
return 0, ErrDuplicateProjectMember
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValidRole(member.Role) {
|
||||||
|
// Return invalid role error
|
||||||
|
return 0, ErrInvalidRole
|
||||||
|
}
|
||||||
|
return c.mgr.AddProjectMember(ctx, member)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidRole(role int) bool {
|
||||||
|
switch role {
|
||||||
|
case common.RoleProjectAdmin,
|
||||||
|
common.RoleMaintainer,
|
||||||
|
common.RoleDeveloper,
|
||||||
|
common.RoleGuest,
|
||||||
|
common.RoleLimitedGuest:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) List(ctx context.Context, projectNameOrID interface{}, entityName string, query *q.Query) ([]*models.Member, error) {
|
||||||
|
p, err := c.projectMgr.Get(ctx, projectNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return nil, errors.BadRequestError(nil).WithMessage("project is not found")
|
||||||
|
}
|
||||||
|
pm := models.Member{
|
||||||
|
ProjectID: p.ProjectID,
|
||||||
|
Entityname: entityName,
|
||||||
|
}
|
||||||
|
return c.mgr.List(ctx, pm, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) Delete(ctx context.Context, projectNameOrID interface{}, memberID int) error {
|
||||||
|
p, err := c.projectMgr.Get(ctx, projectNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.mgr.Delete(ctx, p.ProjectID, memberID)
|
||||||
|
}
|
15
src/controller/member/controller_test.go
Normal file
15
src/controller/member/controller_test.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// 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 member
|
@ -16,6 +16,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@ -30,9 +31,10 @@ import (
|
|||||||
|
|
||||||
"github.com/dghubble/sling"
|
"github.com/dghubble/sling"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/dao/project"
|
|
||||||
common_http "github.com/goharbor/harbor/src/common/http"
|
common_http "github.com/goharbor/harbor/src/common/http"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
|
memberModels "github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
htesting "github.com/goharbor/harbor/src/testing"
|
htesting "github.com/goharbor/harbor/src/testing"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -249,8 +251,8 @@ func prepare() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
ctx := orm.Context()
|
||||||
if projAdminPMID, err = project.AddProjectMember(models.Member{
|
if projAdminPMID, err = member.Mgr.AddProjectMember(ctx, memberModels.Member{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Role: common.RoleProjectAdmin,
|
Role: common.RoleProjectAdmin,
|
||||||
EntityID: int(projAdminID),
|
EntityID: int(projAdminID),
|
||||||
@ -268,8 +270,7 @@ func prepare() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if projAdminRobotPMID, err = member.Mgr.AddProjectMember(ctx, memberModels.Member{
|
||||||
if projAdminRobotPMID, err = project.AddProjectMember(models.Member{
|
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Role: common.RoleProjectAdmin,
|
Role: common.RoleProjectAdmin,
|
||||||
EntityID: int(projAdminRobotID),
|
EntityID: int(projAdminRobotID),
|
||||||
@ -288,7 +289,7 @@ func prepare() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if projDeveloperPMID, err = project.AddProjectMember(models.Member{
|
if projDeveloperPMID, err = member.Mgr.AddProjectMember(ctx, memberModels.Member{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Role: common.RoleDeveloper,
|
Role: common.RoleDeveloper,
|
||||||
EntityID: int(projDeveloperID),
|
EntityID: int(projDeveloperID),
|
||||||
@ -307,7 +308,7 @@ func prepare() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if projGuestPMID, err = project.AddProjectMember(models.Member{
|
if projGuestPMID, err = member.Mgr.AddProjectMember(ctx, memberModels.Member{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Role: common.RoleGuest,
|
Role: common.RoleGuest,
|
||||||
EntityID: int(projGuestID),
|
EntityID: int(projGuestID),
|
||||||
@ -325,7 +326,7 @@ func prepare() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if projLimitedGuestPMID, err = project.AddProjectMember(models.Member{
|
if projLimitedGuestPMID, err = member.Mgr.AddProjectMember(ctx, memberModels.Member{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Role: common.RoleLimitedGuest,
|
Role: common.RoleLimitedGuest,
|
||||||
EntityID: int(projLimitedGuestID),
|
EntityID: int(projLimitedGuestID),
|
||||||
@ -340,7 +341,7 @@ func clean() {
|
|||||||
pmids := []int{projAdminPMID, projDeveloperPMID, projGuestPMID}
|
pmids := []int{projAdminPMID, projDeveloperPMID, projGuestPMID}
|
||||||
|
|
||||||
for _, id := range pmids {
|
for _, id := range pmids {
|
||||||
if err := project.DeleteProjectMemberByID(id); err != nil {
|
if err := member.Mgr.Delete(orm.Context(), 1, id); err != nil {
|
||||||
fmt.Printf("failed to clean up member %d from project library: %v", id, err)
|
fmt.Printf("failed to clean up member %d from project library: %v", id, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,6 @@ func init() {
|
|||||||
beego.Router("/api/projects/:id([0-9]+)/metadatas/?:name", &MetadataAPI{}, "get:Get")
|
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/", &MetadataAPI{}, "post:Post")
|
||||||
beego.Router("/api/projects/:id([0-9]+)/metadatas/:name", &MetadataAPI{}, "put:Put;delete:Delete")
|
beego.Router("/api/projects/:id([0-9]+)/metadatas/:name", &MetadataAPI{}, "put:Put;delete:Delete")
|
||||||
beego.Router("/api/projects/:pid([0-9]+)/members/?:pmid([0-9]+)", &ProjectMemberAPI{})
|
|
||||||
beego.Router("/api/statistics", &StatisticAPI{})
|
beego.Router("/api/statistics", &StatisticAPI{})
|
||||||
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
|
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
|
||||||
beego.Router("/api/labels", &LabelAPI{}, "post:Post;get:List")
|
beego.Router("/api/labels", &LabelAPI{}, "post:Post;get:List")
|
||||||
|
@ -1,307 +0,0 @@
|
|||||||
// Copyright 2018 Project Harbor Authors
|
|
||||||
//
|
|
||||||
// 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"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup/model"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/dao/project"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
|
||||||
"github.com/goharbor/harbor/src/common/rbac"
|
|
||||||
"github.com/goharbor/harbor/src/core/auth"
|
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ProjectMemberAPI handles request to /api/projects/{}/members/{}
|
|
||||||
type ProjectMemberAPI struct {
|
|
||||||
BaseController
|
|
||||||
id int
|
|
||||||
project *models.Project
|
|
||||||
member *models.Member
|
|
||||||
groupType int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrDuplicateProjectMember ...
|
|
||||||
var ErrDuplicateProjectMember = errors.New("The project member specified already exist")
|
|
||||||
|
|
||||||
// ErrInvalidRole ...
|
|
||||||
var ErrInvalidRole = errors.New("Failed to update project member, role is not in 1,2,3")
|
|
||||||
|
|
||||||
// Prepare validates the URL and parms
|
|
||||||
func (pma *ProjectMemberAPI) Prepare() {
|
|
||||||
pma.BaseController.Prepare()
|
|
||||||
|
|
||||||
if !pma.SecurityCtx.IsAuthenticated() {
|
|
||||||
pma.SendUnAuthorizedError(errors.New("Unauthorized"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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.SendBadRequestError(errors.New(text))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pro, err := pma.ProjectCtl.Get(pma.Context(), pid)
|
|
||||||
if err != nil {
|
|
||||||
if errors.IsNotFoundErr(err) {
|
|
||||||
pma.SendNotFoundError(fmt.Errorf("project %d not found", pid))
|
|
||||||
} else {
|
|
||||||
pma.ParseAndHandleError(fmt.Sprintf("failed to get project %d", pid), err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pma.project = pro
|
|
||||||
|
|
||||||
if pma.ParamExistsInPath(":pmid") {
|
|
||||||
pmid, err := pma.GetInt64FromPath(":pmid")
|
|
||||||
if err != nil || pmid <= 0 {
|
|
||||||
pma.SendBadRequestError(fmt.Errorf("The project member id is invalid, pmid:%s", pma.GetStringFromPath(":pmid")))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pma.id = int(pmid)
|
|
||||||
|
|
||||||
members, err := project.GetProjectMember(models.Member{ProjectID: pid, ID: pma.id})
|
|
||||||
if err != nil {
|
|
||||||
pma.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(members) == 0 {
|
|
||||||
pma.SendNotFoundError(fmt.Errorf("project member %d not found in project %d", pmid, pid))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pma.member = members[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
authMode, err := config.AuthMode(orm.Context())
|
|
||||||
if err != nil {
|
|
||||||
pma.SendInternalServerError(fmt.Errorf("failed to get authentication mode"))
|
|
||||||
}
|
|
||||||
if authMode == common.LDAPAuth {
|
|
||||||
pma.groupType = common.LDAPGroupType
|
|
||||||
} else if authMode == common.HTTPAuth {
|
|
||||||
pma.groupType = common.HTTPGroupType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pma *ProjectMemberAPI) requireAccess(action rbac.Action) bool {
|
|
||||||
return pma.RequireProjectAccess(pma.project.ProjectID, action, rbac.ResourceMember)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ...
|
|
||||||
func (pma *ProjectMemberAPI) Get() {
|
|
||||||
projectID := pma.project.ProjectID
|
|
||||||
queryMember := models.Member{}
|
|
||||||
queryMember.ProjectID = projectID
|
|
||||||
pma.Data["json"] = make([]models.Member, 0)
|
|
||||||
if pma.id == 0 {
|
|
||||||
if !pma.requireAccess(rbac.ActionList) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
entityname := pma.GetString("entityname")
|
|
||||||
memberList, err := project.SearchMemberByName(projectID, entityname)
|
|
||||||
if err != nil {
|
|
||||||
pma.SendInternalServerError(fmt.Errorf("Failed to query database for member list, error: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(memberList) > 0 {
|
|
||||||
pma.Data["json"] = memberList
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if !pma.requireAccess(rbac.ActionRead) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pma.Data["json"] = pma.member
|
|
||||||
}
|
|
||||||
pma.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post ... Add a project member
|
|
||||||
func (pma *ProjectMemberAPI) Post() {
|
|
||||||
if !pma.requireAccess(rbac.ActionCreate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectID := pma.project.ProjectID
|
|
||||||
var request models.MemberReq
|
|
||||||
if err := pma.DecodeJSONReq(&request); err != nil {
|
|
||||||
pma.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request.MemberGroup.LdapGroupDN = strings.TrimSpace(request.MemberGroup.LdapGroupDN)
|
|
||||||
|
|
||||||
pmid, err := AddProjectMember(projectID, request)
|
|
||||||
if err == auth.ErrorGroupNotExist || err == auth.ErrorUserNotExist {
|
|
||||||
pma.SendBadRequestError(fmt.Errorf("Failed to add project member, error: %v", err))
|
|
||||||
return
|
|
||||||
} else if err == auth.ErrDuplicateLDAPGroup {
|
|
||||||
pma.SendConflictError(fmt.Errorf("Failed to add project member, already exist group or project member, groupDN:%v", request.MemberGroup.LdapGroupDN))
|
|
||||||
return
|
|
||||||
} else if err == ErrDuplicateProjectMember {
|
|
||||||
pma.SendConflictError(fmt.Errorf("Failed to add project member, already exist group or project member, groupMemberID:%v", request.MemberGroup.ID))
|
|
||||||
return
|
|
||||||
} else if err == ErrInvalidRole {
|
|
||||||
pma.SendBadRequestError(fmt.Errorf("Invalid role ID, role ID %v", request.Role))
|
|
||||||
return
|
|
||||||
} else if err == auth.ErrInvalidLDAPGroupDN {
|
|
||||||
pma.SendBadRequestError(fmt.Errorf("Invalid LDAP DN: %v", request.MemberGroup.LdapGroupDN))
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
pma.SendInternalServerError(fmt.Errorf("Failed to add project member, error: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pma.Redirect(http.StatusCreated, strconv.FormatInt(int64(pmid), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put ... Update an exist project member
|
|
||||||
func (pma *ProjectMemberAPI) Put() {
|
|
||||||
if !pma.requireAccess(rbac.ActionUpdate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pid := pma.project.ProjectID
|
|
||||||
pmID := pma.id
|
|
||||||
var req models.Member
|
|
||||||
if err := pma.DecodeJSONReq(&req); err != nil {
|
|
||||||
pma.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !isValidRole(req.Role) {
|
|
||||||
pma.SendBadRequestError(fmt.Errorf("Invalid role id %v", req.Role))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := project.UpdateProjectMemberRole(pmID, req.Role)
|
|
||||||
if err != nil {
|
|
||||||
pma.SendInternalServerError(fmt.Errorf("Failed to update DB to add project user role, project id: %d, pmid : %d, role id: %d", pid, pmID, req.Role))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete ...
|
|
||||||
func (pma *ProjectMemberAPI) Delete() {
|
|
||||||
if !pma.requireAccess(rbac.ActionDelete) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pmid := pma.id
|
|
||||||
err := project.DeleteProjectMemberByID(pmid)
|
|
||||||
if err != nil {
|
|
||||||
pma.SendInternalServerError(fmt.Errorf("Failed to delete project roles for user, project member id: %d, error: %v", pmid, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddProjectMember ...
|
|
||||||
func AddProjectMember(projectID int64, request models.MemberReq) (int, error) {
|
|
||||||
var member models.Member
|
|
||||||
member.ProjectID = projectID
|
|
||||||
member.Role = request.Role
|
|
||||||
member.EntityType = common.GroupMember
|
|
||||||
|
|
||||||
if request.MemberUser.UserID > 0 {
|
|
||||||
member.EntityID = request.MemberUser.UserID
|
|
||||||
member.EntityType = common.UserMember
|
|
||||||
} else if request.MemberGroup.ID > 0 {
|
|
||||||
member.EntityID = request.MemberGroup.ID
|
|
||||||
} else if len(request.MemberUser.Username) > 0 {
|
|
||||||
var userID int
|
|
||||||
member.EntityType = common.UserMember
|
|
||||||
u, err := dao.GetUser(models.User{Username: request.MemberUser.Username})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if u != nil {
|
|
||||||
userID = u.UserID
|
|
||||||
} else {
|
|
||||||
userID, err = auth.SearchAndOnBoardUser(request.MemberUser.Username)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
member.EntityID = userID
|
|
||||||
} else if len(request.MemberGroup.LdapGroupDN) > 0 {
|
|
||||||
request.MemberGroup.GroupType = common.LDAPGroupType
|
|
||||||
// If groupname provided, use the provided groupname to name this group
|
|
||||||
groupID, err := auth.SearchAndOnBoardGroup(request.MemberGroup.LdapGroupDN, request.MemberGroup.GroupName)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
member.EntityID = groupID
|
|
||||||
} else if len(request.MemberGroup.GroupName) > 0 && request.MemberGroup.GroupType == common.HTTPGroupType || request.MemberGroup.GroupType == common.OIDCGroupType {
|
|
||||||
ugs, err := usergroup.Mgr.List(orm.Context(), model.UserGroup{GroupName: request.MemberGroup.GroupName, GroupType: request.MemberGroup.GroupType})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if len(ugs) == 0 {
|
|
||||||
groupID, err := auth.SearchAndOnBoardGroup(request.MemberGroup.GroupName, "")
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
member.EntityID = groupID
|
|
||||||
} else {
|
|
||||||
member.EntityID = ugs[0].ID
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if member.EntityID <= 0 {
|
|
||||||
return 0, fmt.Errorf("Can not get valid member entity, request: %+v", request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if member already exist in current project
|
|
||||||
memberList, err := project.GetProjectMember(models.Member{
|
|
||||||
ProjectID: member.ProjectID,
|
|
||||||
EntityID: member.EntityID,
|
|
||||||
EntityType: member.EntityType,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if len(memberList) > 0 {
|
|
||||||
return 0, ErrDuplicateProjectMember
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isValidRole(member.Role) {
|
|
||||||
// Return invalid role error
|
|
||||||
return 0, ErrInvalidRole
|
|
||||||
}
|
|
||||||
return project.AddProjectMember(member)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidRole(role int) bool {
|
|
||||||
switch role {
|
|
||||||
case common.RoleProjectAdmin,
|
|
||||||
common.RoleMaintainer,
|
|
||||||
common.RoleDeveloper,
|
|
||||||
common.RoleGuest,
|
|
||||||
common.RoleLimitedGuest:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,414 +0,0 @@
|
|||||||
// Copyright 2018 Project Harbor Authors
|
|
||||||
//
|
|
||||||
// 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"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup/model"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/dao/project"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProjectMemberAPI_Get(t *testing.T) {
|
|
||||||
cases := []*codeCheckingCase{
|
|
||||||
// 401
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
},
|
|
||||||
code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
// 200
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
// 400
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/0/members",
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusBadRequest,
|
|
||||||
},
|
|
||||||
// 200
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: fmt.Sprintf("/api/projects/1/members/%d", projAdminPMID),
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
// 404
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/1/members/121",
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusNotFound,
|
|
||||||
},
|
|
||||||
// 404
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/99999/members/121",
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusNotFound,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
runCodeCheckingCases(t, cases...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProjectMemberAPI_Post(t *testing.T) {
|
|
||||||
userID, err := dao.Register(models.User{
|
|
||||||
Username: "restuser",
|
|
||||||
Password: "Harbor12345",
|
|
||||||
Email: "restuser@example.com",
|
|
||||||
})
|
|
||||||
defer dao.DeleteUser(int(userID))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when create user: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ugList, err := usergroup.Mgr.List(orm.Context(), model.UserGroup{GroupType: 1, LdapGroupDN: "cn=harbor_users,ou=sample,ou=vmware,dc=harbor,dc=com"})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to query the user group")
|
|
||||||
}
|
|
||||||
if len(ugList) <= 0 {
|
|
||||||
t.Errorf("Failed to query the user group")
|
|
||||||
}
|
|
||||||
httpUgList, err := usergroup.Mgr.List(orm.Context(), model.UserGroup{GroupType: 2, GroupName: "vsphere.local\\administrators"})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to query the user group")
|
|
||||||
}
|
|
||||||
if len(httpUgList) <= 0 {
|
|
||||||
t.Errorf("Failed to query the user group")
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := []*codeCheckingCase{
|
|
||||||
// 401
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberUser: models.User{
|
|
||||||
UserID: int(userID),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberUser: models.User{
|
|
||||||
UserID: int(userID),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusCreated,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberUser: models.User{
|
|
||||||
Username: "notexistuser",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusBadRequest,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberUser: models.User{
|
|
||||||
UserID: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusInternalServerError,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/1/members?entityname=restuser",
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
credential: admin,
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberGroup: model.UserGroup{
|
|
||||||
GroupType: 1,
|
|
||||||
LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
code: http.StatusInternalServerError,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
credential: admin,
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberGroup: model.UserGroup{
|
|
||||||
GroupType: 2,
|
|
||||||
ID: httpUgList[0].ID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
code: http.StatusCreated,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
credential: admin,
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberGroup: model.UserGroup{
|
|
||||||
GroupType: 1,
|
|
||||||
ID: ugList[0].ID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
code: http.StatusCreated,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/projects/1/members",
|
|
||||||
credential: admin,
|
|
||||||
bodyJSON: &models.MemberReq{
|
|
||||||
Role: 1,
|
|
||||||
MemberGroup: model.UserGroup{
|
|
||||||
GroupType: 2,
|
|
||||||
GroupName: "vsphere.local/users",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
code: http.StatusInternalServerError,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
runCodeCheckingCases(t, cases...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProjectMemberAPI_PutAndDelete(t *testing.T) {
|
|
||||||
|
|
||||||
userID, err := dao.Register(models.User{
|
|
||||||
Username: "restuser",
|
|
||||||
Password: "Harbor12345",
|
|
||||||
Email: "restuser@example.com",
|
|
||||||
})
|
|
||||||
defer dao.DeleteUser(int(userID))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when create user: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ID, err := project.AddProjectMember(models.Member{
|
|
||||||
ProjectID: 1,
|
|
||||||
Role: 1,
|
|
||||||
EntityID: int(userID),
|
|
||||||
EntityType: "u",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when add project member: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
projectID, err := dao.AddProject(models.Project{Name: "memberputanddelete", OwnerID: 1})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when add project: %v", err)
|
|
||||||
}
|
|
||||||
defer dao.DeleteProject(projectID)
|
|
||||||
|
|
||||||
memberID, err := project.AddProjectMember(models.Member{
|
|
||||||
ProjectID: projectID,
|
|
||||||
Role: 1,
|
|
||||||
EntityID: int(userID),
|
|
||||||
EntityType: "u",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error occurred when add project member: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
URL := fmt.Sprintf("/api/projects/1/members/%v", ID)
|
|
||||||
badURL := fmt.Sprintf("/api/projects/1/members/%v", 0)
|
|
||||||
cases := []*codeCheckingCase{
|
|
||||||
// 401
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: URL,
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
// 200
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: URL,
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: 2,
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
// 200
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: URL,
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: 4,
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
// 400
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: badURL,
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: 2,
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusBadRequest,
|
|
||||||
},
|
|
||||||
// 400
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: URL,
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: -2,
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusBadRequest,
|
|
||||||
},
|
|
||||||
// 404
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: fmt.Sprintf("/api/projects/1/members/%v", memberID),
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: 2,
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusNotFound,
|
|
||||||
},
|
|
||||||
// 200
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodDelete,
|
|
||||||
url: URL,
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
// 404
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodDelete,
|
|
||||||
url: fmt.Sprintf("/api/projects/1/members/%v", memberID),
|
|
||||||
bodyJSON: &models.Member{
|
|
||||||
Role: 2,
|
|
||||||
},
|
|
||||||
credential: admin,
|
|
||||||
},
|
|
||||||
code: http.StatusNotFound,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runCodeCheckingCases(t, cases...)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_isValidRole(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
role int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"project admin", args{1}, true},
|
|
||||||
{"maintainer", args{4}, true},
|
|
||||||
{"developer", args{2}, true},
|
|
||||||
{"guest", args{3}, true},
|
|
||||||
{"limited guest", args{5}, true},
|
|
||||||
{"unknow", args{6}, false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := isValidRole(tt.args.role); got != tt.want {
|
|
||||||
t.Errorf("isValidRole() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,22 +14,22 @@
|
|||||||
package ldap
|
package ldap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/pkg/usergroup"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/config"
|
"github.com/goharbor/harbor/src/controller/config"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup"
|
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup/model"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/dao/project"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/core/api"
|
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
|
memberModels "github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/core/auth"
|
"github.com/goharbor/harbor/src/core/auth"
|
||||||
)
|
)
|
||||||
@ -267,7 +267,7 @@ func TestAuthenticateHelperOnBoardUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOnBoardGroup(t *testing.T) {
|
func TestOnBoardGroup(t *testing.T) {
|
||||||
group := model.UserGroup{
|
group := ugModel.UserGroup{
|
||||||
GroupName: "harbor_group2",
|
GroupName: "harbor_group2",
|
||||||
LdapGroupDN: "cn=harbor_group2,ou=groups,dc=example,dc=com",
|
LdapGroupDN: "cn=harbor_group2,ou=groups,dc=example,dc=com",
|
||||||
}
|
}
|
||||||
@ -360,18 +360,21 @@ func TestSearchAndOnBoardUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
||||||
|
memberMgr := member.Mgr
|
||||||
|
ctx := orm.Context()
|
||||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
currentProject, err := dao.GetProjectByName("member_test_01")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||||
}
|
}
|
||||||
member := models.MemberReq{
|
userID, err := auth.SearchAndOnBoardUser("mike")
|
||||||
ProjectID: currentProject.ProjectID,
|
member := memberModels.Member{
|
||||||
MemberUser: models.User{
|
ProjectID: currentProject.ProjectID,
|
||||||
Username: "mike",
|
EntityType: common.UserMember,
|
||||||
},
|
Entityname: "mike",
|
||||||
Role: common.RoleProjectAdmin,
|
EntityID: userID,
|
||||||
|
Role: common.RoleProjectAdmin,
|
||||||
}
|
}
|
||||||
pmid, err := api.AddProjectMember(currentProject.ProjectID, member)
|
pmid, err := memberMgr.AddProjectMember(ctx, member)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
||||||
}
|
}
|
||||||
@ -383,14 +386,14 @@ func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||||
}
|
}
|
||||||
member2 := models.MemberReq{
|
member2 := memberModels.Member{
|
||||||
ProjectID: currentProject.ProjectID,
|
ProjectID: currentProject.ProjectID,
|
||||||
MemberUser: models.User{
|
EntityType: common.UserMember,
|
||||||
Username: "mike",
|
Entityname: "mike",
|
||||||
},
|
EntityID: userID,
|
||||||
Role: common.RoleProjectAdmin,
|
Role: common.RoleProjectAdmin,
|
||||||
}
|
}
|
||||||
pmid, err = api.AddProjectMember(currentProject.ProjectID, member2)
|
pmid, err = memberMgr.AddProjectMember(ctx, member2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
||||||
}
|
}
|
||||||
@ -399,30 +402,31 @@ func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestAddProjectMemberWithLdapGroup(t *testing.T) {
|
func TestAddProjectMemberWithLdapGroup(t *testing.T) {
|
||||||
|
memberMgr := member.Mgr
|
||||||
|
ctx := orm.Context()
|
||||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
currentProject, err := dao.GetProjectByName("member_test_01")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||||
}
|
}
|
||||||
userGroups := []model.UserGroup{{GroupName: "cn=harbor_users,ou=groups,dc=example,dc=com", LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com", GroupType: common.LDAPGroupType}}
|
userGroups := []ugModel.UserGroup{{GroupName: "cn=harbor_users,ou=groups,dc=example,dc=com", LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com", GroupType: common.LDAPGroupType}}
|
||||||
groupIds, err := usergroup.Mgr.Populate(orm.Context(), userGroups)
|
groupIds, err := usergroup.Mgr.Populate(ctx, userGroups)
|
||||||
member := models.MemberReq{
|
m := memberModels.Member{
|
||||||
ProjectID: currentProject.ProjectID,
|
ProjectID: currentProject.ProjectID,
|
||||||
MemberGroup: model.UserGroup{
|
EntityType: common.GroupMember,
|
||||||
ID: groupIds[0],
|
EntityID: groupIds[0],
|
||||||
},
|
Role: common.RoleProjectAdmin,
|
||||||
Role: common.RoleProjectAdmin,
|
|
||||||
}
|
}
|
||||||
pmid, err := api.AddProjectMember(currentProject.ProjectID, member)
|
pmid, err := memberMgr.AddProjectMember(ctx, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
||||||
}
|
}
|
||||||
if pmid == 0 {
|
if pmid == 0 {
|
||||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid: %v", pmid)
|
t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid: %v", pmid)
|
||||||
}
|
}
|
||||||
queryMember := models.Member{
|
queryMember := memberModels.Member{
|
||||||
ProjectID: currentProject.ProjectID,
|
ProjectID: currentProject.ProjectID,
|
||||||
}
|
}
|
||||||
memberList, err := project.GetProjectMember(queryMember)
|
memberList, err := member.Mgr.List(ctx, queryMember, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to query project member, %v, error: %v", queryMember, err)
|
t.Errorf("Failed to query project member, %v, error: %v", queryMember, err)
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,25 @@ func QuerySetter(ctx context.Context, model interface{}, query *q.Query) (orm.Qu
|
|||||||
return qs, nil
|
return qs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PaginationOnRawSQL append page information to the raw sql
|
||||||
|
// It should be called after the order by
|
||||||
|
// e.g.
|
||||||
|
// select a, b, c from mytable order by a limit ? offset ?
|
||||||
|
// it appends the " limit ? offset ? " to sql,
|
||||||
|
// and appends the limit value and offset value to the params of this query
|
||||||
|
func PaginationOnRawSQL(query *q.Query, sql string, params []interface{}) (string, []interface{}) {
|
||||||
|
if query != nil && query.PageSize > 0 {
|
||||||
|
sql += ` limit ?`
|
||||||
|
params = append(params, query.PageSize)
|
||||||
|
|
||||||
|
if query.PageNumber > 0 {
|
||||||
|
sql += ` offset ?`
|
||||||
|
params = append(params, (query.PageNumber-1)*query.PageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sql, params
|
||||||
|
}
|
||||||
|
|
||||||
// QuerySetterForCount creates the query setter used for count with the sort and pagination information ignored
|
// QuerySetterForCount creates the query setter used for count with the sort and pagination information ignored
|
||||||
func QuerySetterForCount(ctx context.Context, model interface{}, query *q.Query, ignoredCols ...string) (orm.QuerySeter, error) {
|
func QuerySetterForCount(ctx context.Context, model interface{}, query *q.Query, ignoredCols ...string) (orm.QuerySeter, error) {
|
||||||
query = q.MustClone(query)
|
query = q.MustClone(query)
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
. "github.com/goharbor/harbor/src/lib/orm"
|
. "github.com/goharbor/harbor/src/lib/orm"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -410,6 +411,19 @@ func (suite *OrmSuite) TestReadOrCreateParallel() {
|
|||||||
suite.Equal(1, sum)
|
suite.Equal(1, sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *OrmSuite) TestPaginationOnRawSQL() {
|
||||||
|
query := &q.Query{
|
||||||
|
PageNumber: 1,
|
||||||
|
PageSize: 10,
|
||||||
|
}
|
||||||
|
sql := "select * from harbor_user where user_id > ? order by user_name "
|
||||||
|
params := []interface{}{2}
|
||||||
|
sql, params = PaginationOnRawSQL(query, sql, params)
|
||||||
|
suite.Equal("select * from harbor_user where user_id > ? order by user_name limit ? offset ?", sql)
|
||||||
|
suite.Equal(int64(10), params[1])
|
||||||
|
suite.Equal(int64(0), params[2])
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunOrmSuite(t *testing.T) {
|
func TestRunOrmSuite(t *testing.T) {
|
||||||
suite.Run(t, new(OrmSuite))
|
suite.Run(t, new(OrmSuite))
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,14 @@ import (
|
|||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/dao/project"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
proctl "github.com/goharbor/harbor/src/controller/project"
|
proctl "github.com/goharbor/harbor/src/controller/project"
|
||||||
quotactl "github.com/goharbor/harbor/src/controller/quota"
|
quotactl "github.com/goharbor/harbor/src/controller/quota"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
|
memberModels "github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
qtypes "github.com/goharbor/harbor/src/pkg/quota/types"
|
qtypes "github.com/goharbor/harbor/src/pkg/quota/types"
|
||||||
"github.com/goharbor/harbor/src/pkg/repository"
|
"github.com/goharbor/harbor/src/pkg/repository"
|
||||||
)
|
)
|
||||||
@ -105,15 +106,17 @@ func setupTest(t *testing.T) {
|
|||||||
// Add member to project
|
// Add member to project
|
||||||
pmIDs = make([]int, 0)
|
pmIDs = make([]int, 0)
|
||||||
alice.UserID, bob.UserID, eve.UserID = int(aliceID), int(bobID), int(eveID)
|
alice.UserID, bob.UserID, eve.UserID = int(aliceID), int(bobID), int(eveID)
|
||||||
p1m1ID, err := project.AddProjectMember(models.Member{ProjectID: proID1, Role: common.RoleDeveloper, EntityID: int(aliceID), EntityType: common.UserMember})
|
|
||||||
|
p1m1ID, err := member.Mgr.AddProjectMember(ctx, memberModels.Member{ProjectID: proID1, Role: common.RoleDeveloper, EntityID: int(aliceID), EntityType: common.UserMember})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("add project member error %v", err)
|
t.Errorf("add project member error %v", err)
|
||||||
}
|
}
|
||||||
p2m1ID, err := project.AddProjectMember(models.Member{ProjectID: proID2, Role: common.RoleMaintainer, EntityID: int(bobID), EntityType: common.UserMember})
|
p2m1ID, err := member.Mgr.AddProjectMember(ctx, memberModels.Member{ProjectID: proID2, Role: common.RoleMaintainer, EntityID: int(bobID), EntityType: common.UserMember})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("add project member error %v", err)
|
t.Errorf("add project member error %v", err)
|
||||||
}
|
}
|
||||||
p2m2ID, err := project.AddProjectMember(models.Member{ProjectID: proID2, Role: common.RoleMaintainer, EntityID: int(eveID), EntityType: common.UserMember})
|
p2m2ID, err := member.Mgr.AddProjectMember(ctx, memberModels.Member{ProjectID: proID2, Role: common.RoleMaintainer, EntityID: int(eveID), EntityType: common.UserMember})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("add project member error %v", err)
|
t.Errorf("add project member error %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,71 @@
|
|||||||
// Copyright Project Harbor Authors
|
// Copyright Project Harbor Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package project
|
package dao
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetProjectMember gets all members of the project.
|
func init() {
|
||||||
func GetProjectMember(queryMember models.Member) ([]*models.Member, error) {
|
orm.RegisterModel(
|
||||||
|
new(models.Member),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DAO the dao for project member
|
||||||
|
type DAO interface {
|
||||||
|
// GetProjectMember gets all members of the project.
|
||||||
|
GetProjectMember(ctx context.Context, queryMember models.Member, query *q.Query) ([]*models.Member, error)
|
||||||
|
// GetTotalOfProjectMembers returns total of project members
|
||||||
|
GetTotalOfProjectMembers(ctx context.Context, projectID int64, query *q.Query, roles ...int) (int, error)
|
||||||
|
// AddProjectMember inserts a record to table project_member
|
||||||
|
AddProjectMember(ctx context.Context, member models.Member) (int, error)
|
||||||
|
// UpdateProjectMemberRole updates the record in table project_member, only role can be changed
|
||||||
|
UpdateProjectMemberRole(ctx context.Context, projectID int64, pmID int, role int) error
|
||||||
|
// DeleteProjectMemberByID - Delete Project Member by ID
|
||||||
|
DeleteProjectMemberByID(ctx context.Context, projectID int64, pmid int) error
|
||||||
|
// SearchMemberByName search members of the project by entity_name
|
||||||
|
SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error)
|
||||||
|
// ListRoles lists the roles of user for the specific project
|
||||||
|
ListRoles(ctx context.Context, user *models.User, projectID int64) ([]int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dao struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// New ...
|
||||||
|
func New() DAO {
|
||||||
|
return &dao{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dao) GetProjectMember(ctx context.Context, queryMember models.Member, query *q.Query) ([]*models.Member, error) {
|
||||||
log.Debugf("Query condition %+v", queryMember)
|
log.Debugf("Query condition %+v", queryMember)
|
||||||
if queryMember.ProjectID == 0 {
|
if queryMember.ProjectID == 0 {
|
||||||
return nil, fmt.Errorf("Failed to query project member, query condition %v", queryMember)
|
return nil, fmt.Errorf("Failed to query project member, query condition %v", queryMember)
|
||||||
}
|
}
|
||||||
|
o, err := orm.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
o := dao.GetOrmer()
|
|
||||||
sql := ` select a.* from (select pm.id as id, pm.project_id as project_id, ug.id as entity_id, ug.group_name as entity_name, ug.creation_time, ug.update_time, r.name as rolename,
|
sql := ` select a.* from (select pm.id as id, pm.project_id as project_id, ug.id as entity_id, ug.group_name as entity_name, ug.creation_time, ug.update_time, r.name as rolename,
|
||||||
r.role_id as role, pm.entity_type as entity_type from user_group ug join project_member pm
|
r.role_id as role, pm.entity_type as entity_type from user_group ug join project_member pm
|
||||||
on pm.project_id = ? and ug.id = pm.entity_id join role r on pm.role = r.role_id where pm.entity_type = 'g'
|
on pm.project_id = ? and ug.id = pm.entity_id join role r on pm.role = r.role_id where pm.entity_type = 'g'
|
||||||
@ -65,14 +100,14 @@ func GetProjectMember(queryMember models.Member) ([]*models.Member, error) {
|
|||||||
queryParam = append(queryParam, queryMember.ID)
|
queryParam = append(queryParam, queryMember.ID)
|
||||||
}
|
}
|
||||||
sql += ` order by entity_name `
|
sql += ` order by entity_name `
|
||||||
|
sql, queryParam = orm.PaginationOnRawSQL(query, sql, queryParam)
|
||||||
members := []*models.Member{}
|
members := []*models.Member{}
|
||||||
_, err := o.Raw(sql, queryParam).QueryRows(&members)
|
_, err = o.Raw(sql, queryParam).QueryRows(&members)
|
||||||
|
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTotalOfProjectMembers returns total of project members
|
func (d *dao) GetTotalOfProjectMembers(ctx context.Context, projectID int64, query *q.Query, roles ...int) (int, error) {
|
||||||
func GetTotalOfProjectMembers(projectID int64, roles ...int) (int64, error) {
|
|
||||||
log.Debugf("Query condition %+v", projectID)
|
log.Debugf("Query condition %+v", projectID)
|
||||||
if projectID == 0 {
|
if projectID == 0 {
|
||||||
return 0, fmt.Errorf("failed to get total of project members, project id required %v", projectID)
|
return 0, fmt.Errorf("failed to get total of project members, project id required %v", projectID)
|
||||||
@ -87,16 +122,21 @@ func GetTotalOfProjectMembers(projectID int64, roles ...int) (int64, error) {
|
|||||||
queryParam = append(queryParam, roles[0])
|
queryParam = append(queryParam, roles[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
var count int64
|
var count int
|
||||||
err := dao.GetOrmer().Raw(sql, queryParam).QueryRow(&count)
|
o, err := orm.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
o.Raw(sql, queryParam).QueryRow(&count)
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddProjectMember inserts a record to table project_member
|
func (d *dao) AddProjectMember(ctx context.Context, member models.Member) (int, error) {
|
||||||
func AddProjectMember(member models.Member) (int, error) {
|
|
||||||
|
|
||||||
log.Debugf("Adding project member %+v", member)
|
log.Debugf("Adding project member %+v", member)
|
||||||
o := dao.GetOrmer()
|
o, err := orm.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
if member.EntityID <= 0 {
|
if member.EntityID <= 0 {
|
||||||
return 0, fmt.Errorf("Invalid entity_id, member: %+v", member)
|
return 0, fmt.Errorf("Invalid entity_id, member: %+v", member)
|
||||||
@ -107,7 +147,7 @@ func AddProjectMember(member models.Member) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delSQL := "delete from project_member where project_id = ? and entity_id = ? and entity_type = ? "
|
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()
|
_, err = o.Raw(delSQL, member.ProjectID, member.EntityID, member.EntityType).Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -121,27 +161,33 @@ func AddProjectMember(member models.Member) (int, error) {
|
|||||||
return pmid, err
|
return pmid, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProjectMemberRole updates the record in table project_member, only role can be changed
|
func (d *dao) UpdateProjectMemberRole(ctx context.Context, projectID int64, pmID int, role int) error {
|
||||||
func UpdateProjectMemberRole(pmID int, role int) error {
|
o, err := orm.FromContext(ctx)
|
||||||
o := dao.GetOrmer()
|
if err != nil {
|
||||||
sql := "update project_member set role = ? where id = ? "
|
return err
|
||||||
_, err := o.Raw(sql, role, pmID).Exec()
|
}
|
||||||
|
sql := "update project_member set role = ? where project_id = ? and id = ? "
|
||||||
|
_, err = o.Raw(sql, role, projectID, pmID).Exec()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteProjectMemberByID - Delete Project Member by ID
|
func (d *dao) DeleteProjectMemberByID(ctx context.Context, projectID int64, pmid int) error {
|
||||||
func DeleteProjectMemberByID(pmid int) error {
|
o, err := orm.FromContext(ctx)
|
||||||
o := dao.GetOrmer()
|
if err != nil {
|
||||||
sql := "delete from project_member where id = ?"
|
return err
|
||||||
if _, err := o.Raw(sql, pmid).Exec(); err != nil {
|
}
|
||||||
|
sql := "delete from project_member where project_id = ? and id = ?"
|
||||||
|
if _, err := o.Raw(sql, projectID, pmid).Exec(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchMemberByName search members of the project by entity_name
|
func (d *dao) SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error) {
|
||||||
func SearchMemberByName(projectID int64, entityName string) ([]*models.Member, error) {
|
o, err := orm.FromContext(ctx)
|
||||||
o := dao.GetOrmer()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
sql := `select pm.id, pm.project_id,
|
sql := `select pm.id, pm.project_id,
|
||||||
u.username as entity_name,
|
u.username as entity_name,
|
||||||
r.name as rolename,
|
r.name as rolename,
|
||||||
@ -167,12 +213,11 @@ func SearchMemberByName(projectID int64, entityName string) ([]*models.Member, e
|
|||||||
queryParam = append(queryParam, "%"+orm.Escape(entityName)+"%")
|
queryParam = append(queryParam, "%"+orm.Escape(entityName)+"%")
|
||||||
members := []*models.Member{}
|
members := []*models.Member{}
|
||||||
log.Debugf("Query sql: %v", sql)
|
log.Debugf("Query sql: %v", sql)
|
||||||
_, err := o.Raw(sql, queryParam).QueryRows(&members)
|
_, err = o.Raw(sql, queryParam).QueryRows(&members)
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListRoles lists the roles of user for the specific project
|
func (d *dao) ListRoles(ctx context.Context, user *models.User, projectID int64) ([]int, error) {
|
||||||
func ListRoles(user *models.User, projectID int64) ([]int, error) {
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -186,12 +231,16 @@ func ListRoles(user *models.User, projectID int64) ([]int, error) {
|
|||||||
sql += fmt.Sprintf(`union
|
sql += fmt.Sprintf(`union
|
||||||
select role
|
select role
|
||||||
from project_member
|
from project_member
|
||||||
where entity_type = 'g' and entity_id in ( %s ) and project_id = ? `, utils.ParamPlaceholderForIn(len(user.GroupIDs)))
|
where entity_type = 'g' and entity_id in ( %s ) and project_id = ? `, orm.ParamPlaceholderForIn(len(user.GroupIDs)))
|
||||||
params = append(params, user.GroupIDs)
|
params = append(params, user.GroupIDs)
|
||||||
params = append(params, projectID)
|
params = append(params, projectID)
|
||||||
}
|
}
|
||||||
roles := []int{}
|
roles := []int{}
|
||||||
_, err := dao.GetOrmer().Raw(sql, params).QueryRows(&roles)
|
o, err := orm.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = o.Raw(sql, params).QueryRows(&roles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
271
src/pkg/member/dao/dao_test.go
Normal file
271
src/pkg/member/dao/dao_test.go
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// 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/goharbor/harbor/src/common"
|
||||||
|
_ "github.com/goharbor/harbor/src/common/dao"
|
||||||
|
testDao "github.com/goharbor/harbor/src/common/dao"
|
||||||
|
comModels "github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/usergroup"
|
||||||
|
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
||||||
|
htesting "github.com/goharbor/harbor/src/testing"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DaoTestSuite struct {
|
||||||
|
htesting.Suite
|
||||||
|
dao DAO
|
||||||
|
projectMgr project.Manager
|
||||||
|
projectID int64
|
||||||
|
userMgr user.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) SetupSuite() {
|
||||||
|
s.Suite.SetupSuite()
|
||||||
|
s.Suite.ClearTables = []string{"project_member"}
|
||||||
|
s.dao = New()
|
||||||
|
// Extract to test utils
|
||||||
|
initSqls := []string{
|
||||||
|
"insert into harbor_user (username, email, password, realname) values ('member_test_01', 'member_test_01@example.com', '123456', 'member_test_01')",
|
||||||
|
"insert into project (name, owner_id) values ('member_test_01', 1)",
|
||||||
|
"insert into user_group (group_name, group_type, ldap_group_dn) values ('test_group_01', 1, 'CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com')",
|
||||||
|
"update project set owner_id = (select user_id from harbor_user where username = 'member_test_01') where name = 'member_test_01'",
|
||||||
|
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_01') , (select user_id from harbor_user where username = 'member_test_01'), 'u', 1)",
|
||||||
|
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_01') , (select id from user_group where group_name = 'test_group_01'), 'g', 1)",
|
||||||
|
|
||||||
|
"insert into harbor_user (username, email, password, realname) values ('member_test_02', 'member_test_02@example.com', '123456', 'member_test_02')",
|
||||||
|
"insert into project (name, owner_id) values ('member_test_02', 1)",
|
||||||
|
"insert into user_group (group_name, group_type, ldap_group_dn) values ('test_group_02', 1, 'CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com')",
|
||||||
|
"update project set owner_id = (select user_id from harbor_user where username = 'member_test_02') where name = 'member_test_02'",
|
||||||
|
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_02') , (select user_id from harbor_user where username = 'member_test_02'), 'u', 1)",
|
||||||
|
"insert into project_member (project_id, entity_id, entity_type, role) values ( (select project_id from project where name = 'member_test_02') , (select id from user_group where group_name = 'test_group_02'), 'g', 1)",
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSqls := []string{
|
||||||
|
"delete from project where name='member_test_01' or name='member_test_02'",
|
||||||
|
"delete from harbor_user where username='member_test_01' or username='member_test_02' or username='pm_sample'",
|
||||||
|
"delete from user_group",
|
||||||
|
"delete from project_member where id > 1",
|
||||||
|
}
|
||||||
|
testDao.PrepareTestData(clearSqls, initSqls)
|
||||||
|
s.projectMgr = project.Mgr
|
||||||
|
s.userMgr = user.Mgr
|
||||||
|
ctx := s.Context()
|
||||||
|
proj, err := s.projectMgr.Get(ctx, "member_test_01")
|
||||||
|
s.Nil(err)
|
||||||
|
s.NotNil(proj)
|
||||||
|
s.projectID = proj.ProjectID
|
||||||
|
}
|
||||||
|
func (s *DaoTestSuite) TearDownSuite() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) TestAddProjectMember() {
|
||||||
|
ctx := s.Context()
|
||||||
|
proj, err := s.projectMgr.Get(ctx, "member_test_01")
|
||||||
|
s.Nil(err)
|
||||||
|
s.NotNil(proj)
|
||||||
|
|
||||||
|
member := models.Member{
|
||||||
|
ProjectID: proj.ProjectID,
|
||||||
|
EntityID: 1,
|
||||||
|
EntityType: common.UserMember,
|
||||||
|
Role: common.RoleProjectAdmin,
|
||||||
|
}
|
||||||
|
pmid, err := s.dao.AddProjectMember(ctx, member)
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(pmid > 0)
|
||||||
|
|
||||||
|
queryMember := models.Member{
|
||||||
|
ProjectID: proj.ProjectID,
|
||||||
|
ID: pmid,
|
||||||
|
}
|
||||||
|
memberList, err := s.dao.GetProjectMember(ctx, queryMember, nil)
|
||||||
|
s.Nil(err)
|
||||||
|
s.False(len(memberList) == 0)
|
||||||
|
|
||||||
|
_, err = s.dao.AddProjectMember(ctx, models.Member{
|
||||||
|
ProjectID: -1,
|
||||||
|
EntityID: 1,
|
||||||
|
EntityType: common.UserMember,
|
||||||
|
Role: common.RoleProjectAdmin,
|
||||||
|
})
|
||||||
|
|
||||||
|
s.NotNil(err)
|
||||||
|
|
||||||
|
_, err = s.dao.AddProjectMember(ctx, models.Member{
|
||||||
|
ProjectID: 1,
|
||||||
|
EntityID: -1,
|
||||||
|
EntityType: common.UserMember,
|
||||||
|
Role: common.RoleProjectAdmin,
|
||||||
|
})
|
||||||
|
|
||||||
|
s.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) TestUpdateProjectMemberRole() {
|
||||||
|
ctx := s.Context()
|
||||||
|
proj, err := s.projectMgr.Get(ctx, "member_test_01")
|
||||||
|
s.Nil(err)
|
||||||
|
s.NotNil(proj)
|
||||||
|
user := comModels.User{
|
||||||
|
Username: "pm_sample",
|
||||||
|
Email: "pm_sample@example.com",
|
||||||
|
Realname: "pm_sample",
|
||||||
|
Password: "1234567d",
|
||||||
|
}
|
||||||
|
o, err := orm.FromContext(ctx)
|
||||||
|
s.Nil(err)
|
||||||
|
userID, err := o.Insert(&user)
|
||||||
|
s.Nil(err)
|
||||||
|
member := models.Member{
|
||||||
|
ProjectID: proj.ProjectID,
|
||||||
|
EntityID: int(userID),
|
||||||
|
EntityType: common.UserMember,
|
||||||
|
Role: common.RoleProjectAdmin,
|
||||||
|
}
|
||||||
|
|
||||||
|
pmid, err := s.dao.AddProjectMember(ctx, member)
|
||||||
|
s.Nil(err)
|
||||||
|
s.dao.UpdateProjectMemberRole(ctx, proj.ProjectID, pmid, common.RoleDeveloper)
|
||||||
|
|
||||||
|
queryMember := models.Member{
|
||||||
|
ProjectID: proj.ProjectID,
|
||||||
|
EntityID: int(userID),
|
||||||
|
EntityType: common.UserMember,
|
||||||
|
}
|
||||||
|
|
||||||
|
memberList, err := s.dao.GetProjectMember(ctx, queryMember, nil)
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(len(memberList) == 1, "project member should exist")
|
||||||
|
memberItem := memberList[0]
|
||||||
|
s.Equal(common.RoleDeveloper, memberItem.Role, "should be developer role")
|
||||||
|
s.Equal(user.Username, memberItem.Entityname)
|
||||||
|
|
||||||
|
memberList2, err := s.dao.SearchMemberByName(ctx, proj.ProjectID, "pm_sample")
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(len(memberList2) > 0)
|
||||||
|
|
||||||
|
memberList3, err := s.dao.SearchMemberByName(ctx, proj.ProjectID, "")
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(len(memberList3) > 0, "failed to search project member")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) TestGetProjectMembers() {
|
||||||
|
ctx := s.Context()
|
||||||
|
|
||||||
|
query1 := models.Member{ProjectID: s.projectID, Entityname: "member_test_01", EntityType: common.UserMember}
|
||||||
|
member1, err := s.dao.GetProjectMember(ctx, query1, nil)
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(len(member1) > 0)
|
||||||
|
s.Equal(member1[0].Entityname, "member_test_01")
|
||||||
|
|
||||||
|
query2 := models.Member{ProjectID: s.projectID, Entityname: "test_group_01", EntityType: common.GroupMember}
|
||||||
|
member2, err := s.dao.GetProjectMember(ctx, query2, nil)
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(len(member2) > 0)
|
||||||
|
s.Equal(member2[0].Entityname, "test_group_01")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) TestGetTotalOfProjectMembers() {
|
||||||
|
ctx := s.Context()
|
||||||
|
tot, err := s.dao.GetTotalOfProjectMembers(ctx, s.projectID, nil)
|
||||||
|
s.Nil(err)
|
||||||
|
s.Equal(2, int(tot))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) TestListRoles() {
|
||||||
|
ctx := s.Context()
|
||||||
|
|
||||||
|
// nil user
|
||||||
|
roles, err := s.dao.ListRoles(ctx, nil, 1)
|
||||||
|
s.Nil(err)
|
||||||
|
s.Len(roles, 0)
|
||||||
|
|
||||||
|
// user with empty groups
|
||||||
|
u, err := s.userMgr.GetByName(ctx, "member_test_01")
|
||||||
|
s.Nil(err)
|
||||||
|
s.NotNil(u)
|
||||||
|
user := &models.User{
|
||||||
|
UserID: u.UserID,
|
||||||
|
Username: u.Username,
|
||||||
|
}
|
||||||
|
roles, err = s.dao.ListRoles(ctx, user, s.projectID)
|
||||||
|
s.Nil(err)
|
||||||
|
s.Len(roles, 1)
|
||||||
|
|
||||||
|
// user with a group whose ID doesn't exist
|
||||||
|
user.GroupIDs = []int{9999}
|
||||||
|
roles, err = s.dao.ListRoles(ctx, user, s.projectID)
|
||||||
|
s.Nil(err)
|
||||||
|
s.Len(roles, 1)
|
||||||
|
s.Equal(common.RoleProjectAdmin, roles[0])
|
||||||
|
|
||||||
|
// user with a valid group
|
||||||
|
groupID, err := usergroup.Mgr.Create(ctx, ugModel.UserGroup{
|
||||||
|
GroupName: "group_for_list_role",
|
||||||
|
GroupType: 1,
|
||||||
|
LdapGroupDN: "CN=list_role_users,OU=sample,OU=vmware,DC=harbor,DC=com",
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Nil(err)
|
||||||
|
defer usergroup.Mgr.Delete(ctx, groupID)
|
||||||
|
|
||||||
|
memberID, err := s.dao.AddProjectMember(ctx, models.Member{
|
||||||
|
ProjectID: s.projectID,
|
||||||
|
Role: common.RoleDeveloper,
|
||||||
|
EntityID: groupID,
|
||||||
|
EntityType: "g",
|
||||||
|
})
|
||||||
|
s.Nil(err)
|
||||||
|
defer s.dao.DeleteProjectMemberByID(ctx, s.projectID, memberID)
|
||||||
|
|
||||||
|
user.GroupIDs = []int{groupID}
|
||||||
|
roles, err = s.dao.ListRoles(ctx, user, s.projectID)
|
||||||
|
s.Nil(err)
|
||||||
|
s.Len(roles, 2)
|
||||||
|
s.Equal(common.RoleProjectAdmin, roles[0])
|
||||||
|
s.Equal(common.RoleDeveloper, roles[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DaoTestSuite) TestDeleteProjectMember() {
|
||||||
|
ctx := s.Context()
|
||||||
|
var addMember = models.Member{
|
||||||
|
ProjectID: s.projectID,
|
||||||
|
EntityID: 1,
|
||||||
|
EntityType: common.UserMember,
|
||||||
|
Role: common.RoleDeveloper,
|
||||||
|
}
|
||||||
|
pmid, err := s.dao.AddProjectMember(ctx, addMember)
|
||||||
|
s.Nil(err)
|
||||||
|
s.True(pmid > 0)
|
||||||
|
|
||||||
|
err = s.dao.DeleteProjectMemberByID(ctx, s.projectID, pmid)
|
||||||
|
s.Nil(err)
|
||||||
|
|
||||||
|
// not exist
|
||||||
|
err = s.dao.DeleteProjectMemberByID(ctx, s.projectID, -1)
|
||||||
|
s.Nil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaoTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &DaoTestSuite{})
|
||||||
|
}
|
101
src/pkg/member/manager.go
Normal file
101
src/pkg/member/manager.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// 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 member
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member/dao"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Mgr default project member manager
|
||||||
|
Mgr = NewManager()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Manager is used to manage the project member
|
||||||
|
type Manager interface {
|
||||||
|
// AddProjectMember add project member
|
||||||
|
AddProjectMember(ctx context.Context, member models.Member) (int, error)
|
||||||
|
// Delete delete project member
|
||||||
|
Delete(ctx context.Context, projectID int64, memberID int) error
|
||||||
|
// Get get project member by ID
|
||||||
|
Get(ctx context.Context, projectID int64, memberID int) (*models.Member, error)
|
||||||
|
// List list the project member by conditions
|
||||||
|
List(ctx context.Context, queryMember models.Member, query *q.Query) ([]*models.Member, error)
|
||||||
|
// UpdateRole update project member's role
|
||||||
|
UpdateRole(ctx context.Context, projectID int64, pmID int, role int) error
|
||||||
|
// SearchMemberByName search project member by name
|
||||||
|
SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error)
|
||||||
|
// GetTotalOfProjectMembers get the total amount of project members
|
||||||
|
GetTotalOfProjectMembers(ctx context.Context, projectID int64, query *q.Query, roles ...int) (int, error)
|
||||||
|
// ListRoles list project roles
|
||||||
|
ListRoles(ctx context.Context, user *models.User, projectID int64) ([]int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type manager struct {
|
||||||
|
dao dao.DAO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) Get(ctx context.Context, projectID int64, memberID int) (*models.Member, error) {
|
||||||
|
query := models.Member{
|
||||||
|
ID: memberID,
|
||||||
|
ProjectID: projectID,
|
||||||
|
}
|
||||||
|
pm, err := m.dao.GetProjectMember(ctx, query, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(pm) == 0 {
|
||||||
|
return nil, errors.NotFoundError(nil).
|
||||||
|
WithMessage("the project member is not found, project id %v, member id %v", projectID, memberID)
|
||||||
|
}
|
||||||
|
return pm[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) AddProjectMember(ctx context.Context, member models.Member) (int, error) {
|
||||||
|
return m.dao.AddProjectMember(ctx, member)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) UpdateRole(ctx context.Context, projectID int64, pmID int, role int) error {
|
||||||
|
return m.dao.UpdateProjectMemberRole(ctx, projectID, pmID, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error) {
|
||||||
|
return m.dao.SearchMemberByName(ctx, projectID, entityName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) GetTotalOfProjectMembers(ctx context.Context, projectID int64, query *q.Query, roles ...int) (int, error) {
|
||||||
|
return m.dao.GetTotalOfProjectMembers(ctx, projectID, query, roles...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) ListRoles(ctx context.Context, user *models.User, projectID int64) ([]int, error) {
|
||||||
|
return m.dao.ListRoles(ctx, user, projectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) List(ctx context.Context, queryMember models.Member, query *q.Query) ([]*models.Member, error) {
|
||||||
|
return m.dao.GetProjectMember(ctx, queryMember, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) Delete(ctx context.Context, projectID int64, memberID int) error {
|
||||||
|
return m.dao.DeleteProjectMemberByID(ctx, projectID, memberID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManager ...
|
||||||
|
func NewManager() Manager {
|
||||||
|
return &manager{dao: dao.New()}
|
||||||
|
}
|
33
src/pkg/member/models/member.go
Normal file
33
src/pkg/member/models/member.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// 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 models
|
||||||
|
|
||||||
|
// Member holds the details of a member.
|
||||||
|
type Member struct {
|
||||||
|
ID int `orm:"pk;column(id)" json:"id"`
|
||||||
|
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||||
|
Entityname string `orm:"column(entity_name)" json:"entity_name"`
|
||||||
|
Rolename string `json:"role_name"`
|
||||||
|
Role int `json:"role_id"`
|
||||||
|
EntityID int `orm:"column(entity_id)" json:"entity_id"`
|
||||||
|
EntityType string `orm:"column(entity_type)" json:"entity_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User ...
|
||||||
|
type User struct {
|
||||||
|
UserID int
|
||||||
|
Username string
|
||||||
|
GroupIDs []int
|
||||||
|
}
|
@ -39,6 +39,7 @@ func New() http.Handler {
|
|||||||
ScanAllAPI: newScanAllAPI(),
|
ScanAllAPI: newScanAllAPI(),
|
||||||
SearchAPI: newSearchAPI(),
|
SearchAPI: newSearchAPI(),
|
||||||
ProjectAPI: newProjectAPI(),
|
ProjectAPI: newProjectAPI(),
|
||||||
|
MemberAPI: newMemberAPI(),
|
||||||
PreheatAPI: newPreheatAPI(),
|
PreheatAPI: newPreheatAPI(),
|
||||||
IconAPI: newIconAPI(),
|
IconAPI: newIconAPI(),
|
||||||
RobotAPI: newRobotAPI(),
|
RobotAPI: newRobotAPI(),
|
||||||
|
171
src/server/v2.0/handler/member.go
Normal file
171
src/server/v2.0/handler/member.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// 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 handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-openapi/runtime/middleware"
|
||||||
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
|
"github.com/goharbor/harbor/src/controller/member"
|
||||||
|
"github.com/goharbor/harbor/src/lib"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
memberModels "github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
|
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/member"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memberAPI struct {
|
||||||
|
BaseAPI
|
||||||
|
ctl member.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemberAPI() *memberAPI {
|
||||||
|
return &memberAPI{ctl: member.NewController()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memberAPI) CreateProjectMember(ctx context.Context, params operation.CreateProjectMemberParams) middleware.Responder {
|
||||||
|
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
|
||||||
|
if err := m.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionCreate, rbac.ResourceMember); err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
if params.ProjectMember == nil {
|
||||||
|
return m.SendError(ctx, errors.BadRequestError(nil).WithMessage("the project member should provide"))
|
||||||
|
}
|
||||||
|
req, err := toMemberReq(params.ProjectMember)
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
id, err := m.ctl.Create(ctx, projectNameOrID, *req)
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewCreateProjectMemberCreated().
|
||||||
|
WithLocation(fmt.Sprintf("/api/v2.0/projects/%v/members/%d", projectNameOrID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toMemberReq(memberReq *models.ProjectMember) (*member.Request, error) {
|
||||||
|
data, err := json.Marshal(memberReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result member.Request
|
||||||
|
err = json.Unmarshal(data, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memberAPI) DeleteProjectMember(ctx context.Context, params operation.DeleteProjectMemberParams) middleware.Responder {
|
||||||
|
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
|
||||||
|
if err := m.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionDelete, rbac.ResourceMember); err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
if params.Mid == 0 {
|
||||||
|
return m.SendError(ctx, errors.BadRequestError(nil).WithMessage("the project member id is required."))
|
||||||
|
}
|
||||||
|
err := m.ctl.Delete(ctx, projectNameOrID, int(params.Mid))
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewDeleteProjectMemberOK()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memberAPI) GetProjectMember(ctx context.Context, params operation.GetProjectMemberParams) middleware.Responder {
|
||||||
|
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
|
||||||
|
if err := m.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionRead, rbac.ResourceMember); err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Mid == 0 {
|
||||||
|
return m.SendError(ctx, errors.BadRequestError(nil).WithMessage("the member id can not be empty!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
member, err := m.ctl.Get(ctx, projectNameOrID, int(params.Mid))
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewGetProjectMemberOK().WithPayload(toProjectMemberResp(member))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memberAPI) ListProjectMembers(ctx context.Context, params operation.ListProjectMembersParams) middleware.Responder {
|
||||||
|
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
|
||||||
|
if err := m.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionList, rbac.ResourceMember); err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
entityName := lib.StringValue(params.Entityname)
|
||||||
|
query, err := m.BuildQuery(ctx, nil, nil, params.Page, params.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
total, err := m.ctl.Count(ctx, projectNameOrID, query)
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
if total == 0 {
|
||||||
|
return operation.NewListProjectMembersOK().
|
||||||
|
WithXTotalCount(0).
|
||||||
|
WithPayload([]*models.ProjectMemberEntity{})
|
||||||
|
}
|
||||||
|
members, err := m.ctl.List(ctx, projectNameOrID, entityName, query)
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewListProjectMembersOK().
|
||||||
|
WithXTotalCount(int64(total)).
|
||||||
|
WithLink(m.Links(ctx, params.HTTPRequest.URL, int64(total), query.PageNumber, query.PageSize).String()).
|
||||||
|
WithPayload(toProjectMemberRespList(members))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toProjectMemberRespList(members []*memberModels.Member) []*models.ProjectMemberEntity {
|
||||||
|
result := make([]*models.ProjectMemberEntity, 0)
|
||||||
|
for _, mem := range members {
|
||||||
|
result = append(result, toProjectMemberResp(mem))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func toProjectMemberResp(member *memberModels.Member) *models.ProjectMemberEntity {
|
||||||
|
return &models.ProjectMemberEntity{
|
||||||
|
ProjectID: member.ProjectID,
|
||||||
|
ID: int64(member.ID),
|
||||||
|
EntityName: member.Entityname,
|
||||||
|
EntityID: int64(member.EntityID),
|
||||||
|
EntityType: member.EntityType,
|
||||||
|
RoleID: int64(member.Role),
|
||||||
|
RoleName: member.Rolename,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memberAPI) UpdateProjectMember(ctx context.Context, params operation.UpdateProjectMemberParams) middleware.Responder {
|
||||||
|
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
|
||||||
|
if err := m.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionUpdate, rbac.ResourceMember); err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
if params.Role == nil {
|
||||||
|
return m.SendError(ctx, errors.BadRequestError(nil).WithMessage("role can not be empty!"))
|
||||||
|
}
|
||||||
|
if params.Mid == 0 {
|
||||||
|
return m.SendError(ctx, errors.BadRequestError(nil).WithMessage("member id can not be empty!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.ctl.UpdateRole(ctx, projectNameOrID, int(params.Mid), int(params.Role.RoleID))
|
||||||
|
if err != nil {
|
||||||
|
return m.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewUpdateProjectMemberOK()
|
||||||
|
}
|
@ -17,15 +17,16 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/controller/config"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/controller/config"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
|
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
pro "github.com/goharbor/harbor/src/common/dao/project"
|
|
||||||
"github.com/goharbor/harbor/src/common/rbac"
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
"github.com/goharbor/harbor/src/common/security"
|
"github.com/goharbor/harbor/src/common/security"
|
||||||
"github.com/goharbor/harbor/src/common/security/local"
|
"github.com/goharbor/harbor/src/common/security/local"
|
||||||
@ -63,6 +64,7 @@ func newProjectAPI() *projectAPI {
|
|||||||
userMgr: user.Mgr,
|
userMgr: user.Mgr,
|
||||||
repositoryCtl: repository.Ctl,
|
repositoryCtl: repository.Ctl,
|
||||||
projectCtl: project.Ctl,
|
projectCtl: project.Ctl,
|
||||||
|
memberMgr: member.Mgr,
|
||||||
quotaCtl: quota.Ctl,
|
quotaCtl: quota.Ctl,
|
||||||
robotMgr: robot.Mgr,
|
robotMgr: robot.Mgr,
|
||||||
preheatCtl: preheat.Ctl,
|
preheatCtl: preheat.Ctl,
|
||||||
@ -78,6 +80,7 @@ type projectAPI struct {
|
|||||||
userMgr user.Manager
|
userMgr user.Manager
|
||||||
repositoryCtl repository.Controller
|
repositoryCtl repository.Controller
|
||||||
projectCtl project.Controller
|
projectCtl project.Controller
|
||||||
|
memberMgr member.Manager
|
||||||
quotaCtl quota.Controller
|
quotaCtl quota.Controller
|
||||||
robotMgr robot.Manager
|
robotMgr robot.Manager
|
||||||
preheatCtl preheat.Controller
|
preheatCtl preheat.Controller
|
||||||
@ -343,7 +346,7 @@ func (a *projectAPI) GetProjectSummary(ctx context.Context, params operation.Get
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasPerm := a.HasProjectPermission(ctx, p.ProjectID, rbac.ActionList, rbac.ResourceMember); hasPerm {
|
if hasPerm := a.HasProjectPermission(ctx, p.ProjectID, rbac.ActionList, rbac.ResourceMember); hasPerm {
|
||||||
fetchSummaries = append(fetchSummaries, getProjectMemberSummary)
|
fetchSummaries = append(fetchSummaries, a.getProjectMemberSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.IsProxy() {
|
if p.IsProxy() {
|
||||||
@ -696,7 +699,7 @@ func getProjectQuotaSummary(ctx context.Context, p *project.Project, summary *mo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProjectMemberSummary(ctx context.Context, p *project.Project, summary *models.ProjectSummary) {
|
func (a *projectAPI) getProjectMemberSummary(ctx context.Context, p *project.Project, summary *models.ProjectSummary) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for _, e := range []struct {
|
for _, e := range []struct {
|
||||||
@ -712,14 +715,13 @@ func getProjectMemberSummary(ctx context.Context, p *project.Project, summary *m
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(role int, count *int64) {
|
go func(role int, count *int64) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
total, err := a.memberMgr.GetTotalOfProjectMembers(orm.Clone(ctx), p.ProjectID, nil, role)
|
||||||
total, err := pro.GetTotalOfProjectMembers(p.ProjectID, role)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("failed to get total of project members of role %d", role)
|
log.Warningf("failed to get total of project members of role %d", role)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
*count = total
|
*count = int64(total)
|
||||||
}(e.role, e.count)
|
}(e.role, e.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
// RegisterRoutes for Harbor legacy APIs
|
// RegisterRoutes for Harbor legacy APIs
|
||||||
func registerLegacyRoutes() {
|
func registerLegacyRoutes() {
|
||||||
version := APIVersion
|
version := APIVersion
|
||||||
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/members/?:pmid([0-9]+)", &api.ProjectMemberAPI{})
|
|
||||||
beego.Router("/api/"+version+"/email/ping", &api.EmailAPI{}, "post:Ping")
|
beego.Router("/api/"+version+"/email/ping", &api.EmailAPI{}, "post:Ping")
|
||||||
beego.Router("/api/"+version+"/health", &api.HealthAPI{}, "get:CheckHealth")
|
beego.Router("/api/"+version+"/health", &api.HealthAPI{}, "get:CheckHealth")
|
||||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")
|
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")
|
||||||
|
@ -30,8 +30,8 @@ def get_endpoint():
|
|||||||
def _create_client(server, credential, debug, api_type="products"):
|
def _create_client(server, credential, debug, api_type="products"):
|
||||||
cfg = None
|
cfg = None
|
||||||
if api_type in ('projectv2', 'artifact', 'repository', 'scanner', 'scan', 'scanall', 'preheat', 'quota',
|
if api_type in ('projectv2', 'artifact', 'repository', 'scanner', 'scan', 'scanall', 'preheat', 'quota',
|
||||||
'replication', 'registry', 'robot', 'gc', 'retention', "immutable", "system_cve_allowlist",
|
'replication', 'registry', 'robot', 'gc', 'retention', 'immutable', 'system_cve_allowlist',
|
||||||
"configure", "users"):
|
'configure', 'users', 'member'):
|
||||||
cfg = v2_swagger_client.Configuration()
|
cfg = v2_swagger_client.Configuration()
|
||||||
else:
|
else:
|
||||||
cfg = swagger_client.Configuration()
|
cfg = swagger_client.Configuration()
|
||||||
@ -73,6 +73,7 @@ def _create_client(server, credential, debug, api_type="products"):
|
|||||||
"system_cve_allowlist": v2_swagger_client.SystemCVEAllowlistApi(v2_swagger_client.ApiClient(cfg)),
|
"system_cve_allowlist": v2_swagger_client.SystemCVEAllowlistApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"configure": v2_swagger_client.ConfigureApi(v2_swagger_client.ApiClient(cfg)),
|
"configure": v2_swagger_client.ConfigureApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"users": v2_swagger_client.UsersApi(v2_swagger_client.ApiClient(cfg)),
|
"users": v2_swagger_client.UsersApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
|
"member": v2_swagger_client.MemberApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
}.get(api_type,'Error: Wrong API type')
|
}.get(api_type,'Error: Wrong API type')
|
||||||
|
|
||||||
def _assert_status_code(expect_code, return_code, err_msg = r"HTTPS status code s not as we expected. Expected {}, while actual HTTPS status code is {}."):
|
def _assert_status_code(expect_code, return_code, err_msg = r"HTTPS status code s not as we expected. Expected {}, while actual HTTPS status code is {}."):
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import base
|
|
||||||
import swagger_client
|
import swagger_client
|
||||||
import v2_swagger_client
|
import v2_swagger_client
|
||||||
from v2_swagger_client.rest import ApiException
|
|
||||||
from library.base import _assert_status_code
|
from library.base import _assert_status_code
|
||||||
|
from v2_swagger_client.rest import ApiException
|
||||||
|
|
||||||
|
import base
|
||||||
|
|
||||||
|
|
||||||
def is_member_exist_in_project(members, member_user_name, expected_member_role_id = None):
|
def is_member_exist_in_project(members, member_user_name, expected_member_role_id = None):
|
||||||
result = False
|
result = False
|
||||||
@ -125,15 +127,15 @@ class Project(base.Base):
|
|||||||
return count
|
return count
|
||||||
|
|
||||||
def get_project_members(self, project_id, **kwargs):
|
def get_project_members(self, project_id, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
return self._get_client(**kwargs).projects_project_id_members_get(project_id)
|
return self._get_client(**kwargs).list_project_members(project_id)
|
||||||
|
|
||||||
def get_project_member(self, project_id, member_id, expect_status_code = 200, expect_response_body = None, **kwargs):
|
def get_project_member(self, project_id, member_id, expect_status_code = 200, expect_response_body = None, **kwargs):
|
||||||
from swagger_client.rest import ApiException
|
from swagger_client.rest import ApiException
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
data = []
|
data = []
|
||||||
try:
|
try:
|
||||||
data, status_code, _ = self._get_client(**kwargs).projects_project_id_members_mid_get_with_http_info(project_id, member_id,)
|
data, status_code, _ = self._get_client(**kwargs).get_project_member_with_http_info(project_id, member_id,)
|
||||||
except ApiException as e:
|
except ApiException as e:
|
||||||
base._assert_status_code(expect_status_code, e.status)
|
base._assert_status_code(expect_status_code, e.status)
|
||||||
if expect_response_body is not None:
|
if expect_response_body is not None:
|
||||||
@ -145,7 +147,7 @@ class Project(base.Base):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def get_project_member_id(self, project_id, member_user_name, **kwargs):
|
def get_project_member_id(self, project_id, member_user_name, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
members = self.get_project_members(project_id, **kwargs)
|
members = self.get_project_members(project_id, **kwargs)
|
||||||
result = get_member_id_by_name(list(members), member_user_name)
|
result = get_member_id_by_name(list(members), member_user_name)
|
||||||
if result == None:
|
if result == None:
|
||||||
@ -154,36 +156,36 @@ class Project(base.Base):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def check_project_member_not_exist(self, project_id, member_user_name, **kwargs):
|
def check_project_member_not_exist(self, project_id, member_user_name, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
members = self.get_project_members(project_id, **kwargs)
|
members = self.get_project_members(project_id, **kwargs)
|
||||||
result = is_member_exist_in_project(list(members), member_user_name)
|
result = is_member_exist_in_project(list(members), member_user_name)
|
||||||
if result == True:
|
if result == True:
|
||||||
raise Exception(r"User {} should not be a member of project with ID {}.".format(member_user_name, project_id))
|
raise Exception(r"User {} should not be a member of project with ID {}.".format(member_user_name, project_id))
|
||||||
|
|
||||||
def check_project_members_exist(self, project_id, member_user_name, expected_member_role_id = None, **kwargs):
|
def check_project_members_exist(self, project_id, member_user_name, expected_member_role_id = None, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
members = self.get_project_members(project_id, **kwargs)
|
members = self.get_project_members(project_id, **kwargs)
|
||||||
result = is_member_exist_in_project(members, member_user_name, expected_member_role_id = expected_member_role_id)
|
result = is_member_exist_in_project(members, member_user_name, expected_member_role_id = expected_member_role_id)
|
||||||
if result == False:
|
if result == False:
|
||||||
raise Exception(r"User {} should be a member of project with ID {}.".format(member_user_name, project_id))
|
raise Exception(r"User {} should be a member of project with ID {}.".format(member_user_name, project_id))
|
||||||
|
|
||||||
def update_project_member_role(self, project_id, member_id, member_role_id, expect_status_code = 200, **kwargs):
|
def update_project_member_role(self, project_id, member_id, member_role_id, expect_status_code = 200, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
role = swagger_client.Role(role_id = member_role_id)
|
role = swagger_client.Role(role_id = member_role_id)
|
||||||
data, status_code, _ = self._get_client(**kwargs).projects_project_id_members_mid_put_with_http_info(project_id, member_id, role = role)
|
data, status_code, _ = self._get_client(**kwargs).update_project_member_with_http_info(project_id, member_id, role = role)
|
||||||
base._assert_status_code(expect_status_code, status_code)
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
base._assert_status_code(200, status_code)
|
base._assert_status_code(200, status_code)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def delete_project_member(self, project_id, member_id, expect_status_code = 200, **kwargs):
|
def delete_project_member(self, project_id, member_id, expect_status_code = 200, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
_, status_code, _ = self._get_client(**kwargs).projects_project_id_members_mid_delete_with_http_info(project_id, member_id)
|
_, status_code, _ = self._get_client(**kwargs).delete_project_member_with_http_info(project_id, member_id)
|
||||||
base._assert_status_code(expect_status_code, status_code)
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
base._assert_status_code(200, status_code)
|
base._assert_status_code(200, status_code)
|
||||||
|
|
||||||
def add_project_members(self, project_id, user_id = None, member_role_id = None, _ldap_group_dn=None, expect_status_code = 201, **kwargs):
|
def add_project_members(self, project_id, user_id = None, member_role_id = None, _ldap_group_dn=None, expect_status_code = 201, **kwargs):
|
||||||
kwargs['api_type'] = 'products'
|
kwargs['api_type'] = 'member'
|
||||||
projectMember = swagger_client.ProjectMember()
|
projectMember = v2_swagger_client.ProjectMember()
|
||||||
if user_id is not None:
|
if user_id is not None:
|
||||||
projectMember.member_user = {"user_id": int(user_id)}
|
projectMember.member_user = {"user_id": int(user_id)}
|
||||||
if member_role_id is None:
|
if member_role_id is None:
|
||||||
@ -191,12 +193,12 @@ class Project(base.Base):
|
|||||||
else:
|
else:
|
||||||
projectMember.role_id = member_role_id
|
projectMember.role_id = member_role_id
|
||||||
if _ldap_group_dn is not None:
|
if _ldap_group_dn is not None:
|
||||||
projectMember.member_group = swagger_client.UserGroup(ldap_group_dn=_ldap_group_dn)
|
projectMember.member_group = v2_swagger_client.UserGroup(ldap_group_dn=_ldap_group_dn)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
try:
|
try:
|
||||||
data, status_code, header = self._get_client(**kwargs).projects_project_id_members_post_with_http_info(project_id, project_member = projectMember)
|
data, status_code, header = self._get_client(**kwargs).create_project_member_with_http_info(project_id, project_member = projectMember)
|
||||||
except swagger_client.rest.ApiException as e:
|
except ApiException as e:
|
||||||
base._assert_status_code(expect_status_code, e.status)
|
base._assert_status_code(expect_status_code, e.status)
|
||||||
else:
|
else:
|
||||||
base._assert_status_code(expect_status_code, status_code)
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
Loading…
Reference in New Issue
Block a user