Merge pull request #14544 from stonezdj/21mar26_usergroup

Refactor user group to new programming model
This commit is contained in:
stonezdj(Daojun Zhang) 2021-04-09 16:08:35 +08:00 committed by GitHub
commit 4e7b530140
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1108 additions and 1302 deletions

View File

@ -883,131 +883,6 @@ paths:
description: The resource does not exist.
'500':
description: Unexpected internal errors.
/usergroups:
get:
summary: Get all user groups information
description: Get all user groups information
tags:
- Products
responses:
'200':
description: Get user group successfully.
schema:
type: array
items:
$ref: '#/definitions/UserGroup'
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the user group.
'500':
description: Unexpected internal errors.
post:
summary: Create user group
description: Create user group information
tags:
- Products
parameters:
- name: usergroup
in: body
schema:
$ref: '#/definitions/UserGroup'
responses:
'201':
description: User group created successfully.
headers:
Location:
type: string
description: The URL of the created resource
'400':
description: Invalid LDAP group DN.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the user group.
'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.
'/usergroups/{group_id}':
get:
summary: Get user group information
description: Get user group information
tags:
- Products
parameters:
- name: group_id
in: path
type: integer
format: int64
required: true
description: Group ID
responses:
'200':
description: User group get successfully.
schema:
$ref: '#/definitions/UserGroup'
'400':
description: The user group id is invalid.
'401':
description: User need to log in first.
'403':
description: User in session does not have permission to the user group.
'404':
description: User group does not exist.
'500':
description: Unexpected internal errors.
put:
summary: Update group information
description: Update user group information
tags:
- Products
parameters:
- name: group_id
in: path
type: integer
format: int64
required: true
description: Group ID
- name: usergroup
in: body
required: false
schema:
$ref: '#/definitions/UserGroup'
responses:
'200':
description: User group updated successfully.
'400':
description: The user group id is invalid.
'401':
description: User need to log in first.
'403':
description: Only admin has this authority.
'404':
description: User group does not exist.
'500':
description: Unexpected internal errors.
delete:
summary: Delete user group
description: Delete user group
tags:
- Products
parameters:
- name: group_id
type: integer
in: path
required: true
responses:
'200':
description: User group deleted successfully.
'400':
description: The user group id is invalid.
'401':
description: User need to log in first.
'403':
description: Only admin has this authority.
'500':
description: Unexpected internal errors.
/email/ping:
post:
summary: Test connection and authentication with email server.

View File

@ -2293,6 +2293,142 @@ paths:
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/usergroups:
get:
summary: Get all user groups information
description: Get all user groups information
operationId: listUserGroups
tags:
- usergroup
parameters:
- $ref: '#/parameters/requestId'
responses:
'200':
description: Get user group successfully.
schema:
type: array
items:
$ref: '#/definitions/UserGroup'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
post:
summary: Create user group
description: Create user group information
operationId: createUserGroup
tags:
- usergroup
parameters:
- $ref: '#/parameters/requestId'
- name: usergroup
in: body
schema:
$ref: '#/definitions/UserGroup'
responses:
'201':
description: User group 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'
'/usergroups/{group_id}':
get:
summary: Get user group information
description: Get user group information
operationId: getUserGroup
tags:
- usergroup
parameters:
- $ref: '#/parameters/requestId'
- name: group_id
in: path
type: integer
format: int64
required: true
description: Group ID
responses:
'200':
description: User group get successfully.
schema:
$ref: '#/definitions/UserGroup'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'500':
$ref: '#/responses/500'
put:
summary: Update group information
description: Update user group information
operationId: updateUserGroup
tags:
- usergroup
parameters:
- $ref: '#/parameters/requestId'
- name: group_id
in: path
type: integer
format: int64
required: true
description: Group ID
- name: usergroup
in: body
required: false
schema:
$ref: '#/definitions/UserGroup'
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 user group
description: Delete user group
operationId: deleteUserGroup
tags:
- usergroup
parameters:
- $ref: '#/parameters/requestId'
- name: group_id
type: integer
in: path
required: true
responses:
'200':
$ref: '#/responses/200'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/icons/{digest}:
get:
summary: Get artifact icon

View File

@ -1,170 +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 group
import (
"time"
"github.com/goharbor/harbor/src/common/utils"
"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/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
)
// ErrGroupNameDup ...
var ErrGroupNameDup = errors.New("duplicated user group name")
// AddUserGroup - Add User Group
func AddUserGroup(userGroup models.UserGroup) (int, error) {
userGroupList, err := QueryUserGroup(models.UserGroup{GroupName: userGroup.GroupName, GroupType: common.HTTPGroupType})
if err != nil {
return 0, ErrGroupNameDup
}
if len(userGroupList) > 0 {
return 0, ErrGroupNameDup
}
o := dao.GetOrmer()
sql := "insert into user_group (group_name, group_type, ldap_group_dn, creation_time, update_time) values (?, ?, ?, ?, ?) RETURNING id"
var id int
now := time.Now()
err = o.Raw(sql, userGroup.GroupName, userGroup.GroupType, utils.TrimLower(userGroup.LdapGroupDN), now, now).QueryRow(&id)
if err != nil {
return 0, err
}
return id, nil
}
// QueryUserGroup - Query User Group
func QueryUserGroup(query models.UserGroup) ([]*models.UserGroup, error) {
o := dao.GetOrmer()
sql := `select id, group_name, group_type, ldap_group_dn from user_group where 1=1 `
sqlParam := make([]interface{}, 1)
var groups []*models.UserGroup
if len(query.GroupName) != 0 {
sql += ` and group_name = ? `
sqlParam = append(sqlParam, query.GroupName)
}
if query.GroupType != 0 {
sql += ` and group_type = ? `
sqlParam = append(sqlParam, query.GroupType)
}
if len(query.LdapGroupDN) != 0 {
sql += ` and ldap_group_dn = ? `
sqlParam = append(sqlParam, utils.TrimLower(query.LdapGroupDN))
}
if query.ID != 0 {
sql += ` and id = ? `
sqlParam = append(sqlParam, query.ID)
}
_, err := o.Raw(sql, sqlParam).QueryRows(&groups)
if err != nil {
return nil, err
}
return groups, nil
}
// GetUserGroup ...
func GetUserGroup(id int) (*models.UserGroup, error) {
userGroup := models.UserGroup{ID: id}
userGroupList, err := QueryUserGroup(userGroup)
if err != nil {
return nil, err
}
if len(userGroupList) > 0 {
return userGroupList[0], nil
}
return nil, nil
}
// PopulateGroup - Return the group ID by given group name. if not exist in Harbor DB, create one
func PopulateGroup(userGroups []models.UserGroup) ([]int, error) {
ugList := make([]int, 0)
for _, group := range userGroups {
err := OnBoardUserGroup(&group)
if err != nil {
return ugList, err
}
if group.ID > 0 {
ugList = append(ugList, group.ID)
}
}
return ugList, nil
}
// DeleteUserGroup ...
func DeleteUserGroup(id int) error {
userGroup := models.UserGroup{ID: id}
o := dao.GetOrmer()
_, err := o.Delete(&userGroup)
if err == nil {
// Delete all related project members
sql := `delete from project_member where entity_id = ? and entity_type='g'`
_, err := o.Raw(sql, id).Exec()
if err != nil {
return err
}
}
return err
}
// UpdateUserGroupName ...
func UpdateUserGroupName(id int, groupName string) error {
log.Debugf("Updating user_group with id:%v, name:%v", id, groupName)
o := dao.GetOrmer()
sql := "update user_group set group_name = ? where id = ? "
_, err := o.Raw(sql, groupName, id).Exec()
return err
}
func onBoardCommonUserGroup(g *models.UserGroup, keyAttribute string, combinedKeyAttributes ...string) error {
g.LdapGroupDN = utils.TrimLower(g.LdapGroupDN)
o := dao.GetOrmer()
created, ID, err := o.ReadOrCreate(g, keyAttribute, combinedKeyAttributes...)
if err != nil {
return err
}
if created {
g.ID = int(ID)
} else {
prevGroup, err := GetUserGroup(int(ID))
if err != nil {
return err
}
g.ID = prevGroup.ID
g.GroupName = prevGroup.GroupName
g.GroupType = prevGroup.GroupType
g.LdapGroupDN = prevGroup.LdapGroupDN
}
return nil
}
// OnBoardUserGroup will check if a usergroup exists in usergroup table, if not insert the usergroup and
// put the id in the pointer of usergroup model, if it does exist, return the usergroup's profile.
func OnBoardUserGroup(g *models.UserGroup) error {
if g.GroupType == common.LDAPGroupType {
return onBoardCommonUserGroup(g, "LdapGroupDN", "GroupType")
}
return onBoardCommonUserGroup(g, "GroupName", "GroupType")
}

View File

@ -1,495 +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 group
import (
"fmt"
"os"
"testing"
"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/lib/log"
"github.com/stretchr/testify/assert"
)
var createdUserGroupID int
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 harbor_user (username, email, password, realname) values ('grouptestu09', 'grouptestu09@example.com', '123456', 'grouptestu09')",
"insert into project (name, owner_id) values ('member_test_01', 1)",
`insert into project (name, owner_id) values ('group_project2', 1)`,
`insert into project (name, owner_id) values ('group_project_private', 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')",
"insert into user_group (group_name, group_type, ldap_group_dn) values ('sync_user_group4', 1, 'cn=sync_user_group4,dc=example,dc=com')",
"insert into user_group (group_name, group_type, ldap_group_dn) values ('test_http_group', 2, '')",
"insert into user_group (group_name, group_type, ldap_group_dn) values ('test_myhttp_group', 2, '')",
"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 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_http_group'), 'g', 4)",
"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_myhttp_group'), 'g', 4)",
}
clearSqls := []string{
"delete from project where name='member_test_01'",
"delete from project where name='group_project2'",
"delete from project where name='group_project_private'",
"delete from harbor_user where username='member_test_01' or username='pm_sample' or username='grouptestu09'",
"delete from user_group",
"delete from project_member where id > 1",
}
dao.ExecuteBatchSQL(initSqls)
defer dao.ExecuteBatchSQL(clearSqls)
result = m.Run()
if result != 0 {
os.Exit(result)
}
}
}
func TestAddUserGroup(t *testing.T) {
type args struct {
userGroup models.UserGroup
}
tests := []struct {
name string
args args
want int
wantErr bool
}{
{"Insert an ldap user group", args{userGroup: models.UserGroup{GroupName: "sample_group", GroupType: common.LDAPGroupType, LdapGroupDN: "sample_ldap_dn_string"}}, 0, false},
{"Insert other user group", args{userGroup: models.UserGroup{GroupName: "other_group", GroupType: 3, LdapGroupDN: "other information"}}, 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := AddUserGroup(tt.args.userGroup)
if (err != nil) != tt.wantErr {
t.Errorf("AddUserGroup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got <= 0 {
t.Errorf("Failed to add user group")
}
})
}
}
func TestQueryUserGroup(t *testing.T) {
type args struct {
query models.UserGroup
}
tests := []struct {
name string
args args
want int
wantErr bool
}{
{"Query all user group", args{query: models.UserGroup{GroupName: "test_group_01"}}, 1, false},
{"Query all ldap group", args{query: models.UserGroup{GroupType: common.LDAPGroupType}}, 3, false},
{"Query ldap group with group property", args{query: models.UserGroup{GroupType: common.LDAPGroupType, LdapGroupDN: "CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com"}}, 1, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := QueryUserGroup(tt.args.query)
if (err != nil) != tt.wantErr {
t.Errorf("QueryUserGroup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.want {
t.Errorf("QueryUserGroup() = %v, want %v", len(got), tt.want)
}
})
}
}
func TestGetUserGroup(t *testing.T) {
userGroup := models.UserGroup{GroupName: "insert_group", GroupType: common.LDAPGroupType, LdapGroupDN: "ldap_dn_string"}
result, err := AddUserGroup(userGroup)
if err != nil {
t.Errorf("Error occurred when AddUserGroup: %v", err)
}
createdUserGroupID = result
type args struct {
id int
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{"Get User Group", args{id: result}, "insert_group", false},
{"Get User Group does not exist", args{id: 9999}, "insert_group", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetUserGroup(tt.args.id)
if (err != nil) != tt.wantErr {
t.Errorf("GetUserGroup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != nil && got.GroupName != tt.want {
t.Errorf("GetUserGroup() = %v, want %v", got.GroupName, tt.want)
}
})
}
}
func TestUpdateUserGroup(t *testing.T) {
if createdUserGroupID == 0 {
fmt.Println("User group doesn't created, skip to test!")
return
}
type args struct {
id int
groupName string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"Update user group", args{id: createdUserGroupID, groupName: "updated_groupname"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Printf("id=%v\n", createdUserGroupID)
if err := UpdateUserGroupName(tt.args.id, tt.args.groupName); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserGroup() error = %v, wantErr %v", err, tt.wantErr)
userGroup, err := GetUserGroup(tt.args.id)
if err != nil {
t.Errorf("Error occurred when GetUserGroup: %v", err)
}
if userGroup == nil {
t.Fatalf("Failed to get updated user group")
}
if userGroup.GroupName != tt.args.groupName {
t.Fatalf("Failed to update user group")
}
}
})
}
}
func TestDeleteUserGroup(t *testing.T) {
if createdUserGroupID == 0 {
fmt.Println("User group doesn't created, skip to test!")
return
}
type args struct {
id int
}
tests := []struct {
name string
args args
wantErr bool
}{
{"Delete existing user group", args{id: createdUserGroupID}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := DeleteUserGroup(tt.args.id); (err != nil) != tt.wantErr {
t.Errorf("DeleteUserGroup() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestOnBoardUserGroup(t *testing.T) {
type args struct {
g *models.UserGroup
}
tests := []struct {
name string
args args
wantErr bool
}{
{"OnBoardUserGroup",
args{g: &models.UserGroup{
GroupName: "harbor_example",
LdapGroupDN: "cn=harbor_example,ou=groups,dc=example,dc=com",
GroupType: common.LDAPGroupType}},
false},
{"OnBoardUserGroup second time",
args{g: &models.UserGroup{
GroupName: "harbor_example",
LdapGroupDN: "cn=harbor_example,ou=groups,dc=example,dc=com",
GroupType: common.LDAPGroupType}},
false},
{"OnBoardUserGroup HTTP user group",
args{g: &models.UserGroup{
GroupName: "test_myhttp_group",
GroupType: common.HTTPGroupType}},
false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := OnBoardUserGroup(tt.args.g); (err != nil) != tt.wantErr {
t.Errorf("OnBoardUserGroup() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestGetGroupProjects(t *testing.T) {
projectID1, err := dao.AddProject(models.Project{
Name: "grouptest01",
OwnerID: 1,
})
if err != nil {
t.Errorf("Error occurred when AddProject: %v", err)
}
defer dao.DeleteProject(projectID1)
projectID2, err := dao.AddProject(models.Project{
Name: "grouptest02",
OwnerID: 1,
})
if err != nil {
t.Errorf("Error occurred when AddProject: %v", err)
}
groupID, err := AddUserGroup(models.UserGroup{
GroupName: "test_group_03",
GroupType: 1,
LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com",
})
if err != nil {
t.Errorf("Error occurred when AddUserGroup: %v", err)
}
defer DeleteUserGroup(groupID)
pmid, err := project.AddProjectMember(models.Member{
ProjectID: projectID1,
EntityID: groupID,
EntityType: "g",
})
defer project.DeleteProjectMemberByID(pmid)
pmid2, err := project.AddProjectMember(models.Member{
ProjectID: projectID2,
EntityID: groupID,
EntityType: "g",
})
defer project.DeleteProjectMemberByID(pmid2)
if err := dao.DeleteProject(projectID2); err != nil {
t.Errorf("Error occurred when DeleteProject: %v", err)
}
type args struct {
query *models.ProjectQueryParam
groupIDs []int
}
member := &models.MemberQuery{
Name: "grouptestu09",
}
tests := []struct {
name string
args args
wantSize int
wantErr bool
}{
{"Query with group DN",
args{
query: &models.ProjectQueryParam{
Member: member,
},
groupIDs: []int{groupID},
},
1, false},
{"Query without group DN",
args{
query: &models.ProjectQueryParam{
Member: member,
},
groupIDs: []int{},
},
0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := dao.GetGroupProjects(tt.args.groupIDs, tt.args.query)
if (err != nil) != tt.wantErr {
t.Errorf("GetGroupProjects() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.wantSize {
t.Errorf("GetGroupProjects() size: %v, want %v", len(got), tt.wantSize)
}
})
}
}
func TestGetTotalGroupProjects(t *testing.T) {
projectID1, err := dao.AddProject(models.Project{
Name: "grouptest01",
OwnerID: 1,
})
if err != nil {
t.Errorf("Error occurred when AddProject: %v", err)
}
defer dao.DeleteProject(projectID1)
projectID2, err := dao.AddProject(models.Project{
Name: "grouptest02",
OwnerID: 1,
})
if err != nil {
t.Errorf("Error occurred when AddProject: %v", err)
}
groupID, err := AddUserGroup(models.UserGroup{
GroupName: "test_group_05",
GroupType: 1,
LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com",
})
if err != nil {
t.Errorf("Error occurred when AddUserGroup: %v", err)
}
defer DeleteUserGroup(groupID)
pmid, err := project.AddProjectMember(models.Member{
ProjectID: projectID1,
EntityID: groupID,
EntityType: "g",
})
defer project.DeleteProjectMemberByID(pmid)
pmid2, err := project.AddProjectMember(models.Member{
ProjectID: projectID2,
EntityID: groupID,
EntityType: "g",
})
defer project.DeleteProjectMemberByID(pmid2)
if err := dao.DeleteProject(projectID2); err != nil { // deleted project should not be counted
t.Errorf("Error occurred when delete project: %v", err)
}
type args struct {
query *models.ProjectQueryParam
groupIDs []int
}
tests := []struct {
name string
args args
wantSize int
wantErr bool
}{
{"Query with group ID",
args{
query: &models.ProjectQueryParam{
Member: &models.MemberQuery{
Name: "member_test_01",
},
},
groupIDs: []int{groupID},
},
2, false},
{"Query without group ID",
args{
query: &models.ProjectQueryParam{
Member: &models.MemberQuery{
Name: "member_test_01",
},
},
groupIDs: []int{}},
1, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := dao.GetTotalGroupProjects(tt.args.groupIDs, tt.args.query)
if (err != nil) != tt.wantErr {
t.Errorf("GetGroupProjects() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.wantSize {
t.Errorf("GetGroupProjects() size: %v, want %v", got, tt.wantSize)
}
})
}
}
func TestSyncGroupByGroupKey(t *testing.T) {
type args []models.UserGroup
type result struct {
wantError bool
}
cases := []struct {
name string
in args
want result
}{
{
name: `normal test http group`,
in: args{
models.UserGroup{GroupName: "orange", GroupType: common.HTTPGroupType},
models.UserGroup{GroupName: "apple", GroupType: common.HTTPGroupType},
models.UserGroup{GroupName: "pearl", GroupType: common.HTTPGroupType}},
want: result{false},
},
{
name: `normal test oidc group`,
in: args{
models.UserGroup{GroupName: "dog", GroupType: common.OIDCGroupType},
models.UserGroup{GroupName: "cat", GroupType: common.OIDCGroupType},
models.UserGroup{GroupName: "bee", GroupType: common.OIDCGroupType},
},
want: result{false},
},
{
name: `normal test oidc group`,
in: args{
models.UserGroup{GroupName: "cn=sync_user_group1,dc=example,dc=com", LdapGroupDN: "cn=sync_user_group1,dc=example,dc=com", GroupType: common.LDAPGroupType},
models.UserGroup{GroupName: "cn=sync_user_group2,dc=example,dc=com", LdapGroupDN: "cn=sync_user_group2,dc=example,dc=com", GroupType: common.LDAPGroupType},
models.UserGroup{GroupName: "cn=sync_user_group3,dc=example,dc=com", LdapGroupDN: "cn=sync_user_group3,dc=example,dc=com", GroupType: common.LDAPGroupType},
models.UserGroup{GroupName: "cn=sync_user_group4,dc=example,dc=com", LdapGroupDN: "cn=sync_user_group4,dc=example,dc=com", GroupType: common.LDAPGroupType},
},
want: result{false},
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
got, err := PopulateGroup(tt.in)
if err != nil && !tt.want.wantError {
t.Errorf("error %v", err)
}
if !assert.Equal(t, len(tt.in), len(got)) {
t.Errorf(`(%v) != %v; want "%v"`, len(tt.in), len(got), len(tt.in))
}
for _, id := range got {
DeleteUserGroup(id)
}
})
}
}

View File

@ -16,11 +16,14 @@ package project
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"os"
"testing"
"github.com/goharbor/harbor/src/common/dao/group"
"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"
@ -351,15 +354,15 @@ func TestListRoles(t *testing.T) {
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 := group.AddUserGroup(models.UserGroup{
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 group.DeleteUserGroup(groupID)
defer usergroup.Mgr.Delete(orm.Context(), groupID)
memberID, err := AddProjectMember(models.Member{
ProjectID: project.ProjectID,

View File

@ -27,7 +27,6 @@ func init() {
new(ProjectMetadata),
new(Label),
new(ResourceLabel),
new(UserGroup),
new(JobLog),
new(OIDCUser),
new(ProjectBlob),

View File

@ -14,6 +14,10 @@
package models
import (
"github.com/goharbor/harbor/src/pkg/usergroup/model"
)
// Member holds the details of a member.
type Member struct {
ID int `orm:"pk;column(id)" json:"id"`
@ -27,8 +31,8 @@ type Member struct {
// MemberReq - Create Project Member Request
type MemberReq struct {
ProjectID int64 `json:"project_id"`
Role int `json:"role_id,omitempty"`
MemberUser User `json:"member_user,omitempty"`
MemberGroup UserGroup `json:"member_group,omitempty"`
ProjectID int64 `json:"project_id"`
Role int `json:"role_id,omitempty"`
MemberUser User `json:"member_user,omitempty"`
MemberGroup model.UserGroup `json:"member_group,omitempty"`
}

View File

@ -0,0 +1,111 @@
// 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 usergroup
import (
"context"
"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/pkg/ldap"
"github.com/goharbor/harbor/src/pkg/usergroup"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
)
var (
// Ctl Global instance of the UserGroup controller
Ctl = newController()
)
// Controller manages the user group
type Controller interface {
// Delete delete user group
Delete(ctx context.Context, id int) error
// Update update the user group name
Update(ctx context.Context, id int, groupName string) error
// Create create user group
Create(ctx context.Context, group model.UserGroup) (int, error)
// Get get user group by id
Get(ctx context.Context, id int) (*model.UserGroup, error)
// Ensure if this user group doesn't exist in Harbor, create it in Harbor, if exist, do nothing
Ensure(ctx context.Context, group *model.UserGroup) error
// Populate populate user group and get the user group's id
Populate(ctx context.Context, userGroups []model.UserGroup) ([]int, error)
// List list user groups
List(ctx context.Context, userGroup model.UserGroup) ([]*model.UserGroup, error)
}
type controller struct {
mgr usergroup.Manager
}
func newController() Controller {
return &controller{mgr: usergroup.Mgr}
}
func (c *controller) List(ctx context.Context, userGroup model.UserGroup) ([]*model.UserGroup, error) {
return c.mgr.List(ctx, userGroup)
}
func (c *controller) Populate(ctx context.Context, userGroups []model.UserGroup) ([]int, error) {
return c.mgr.Populate(ctx, userGroups)
}
func (c *controller) Ensure(ctx context.Context, group *model.UserGroup) error {
return c.mgr.Onboard(ctx, group)
}
func (c *controller) Delete(ctx context.Context, id int) error {
return c.mgr.Delete(ctx, id)
}
func (c *controller) Update(ctx context.Context, id int, groupName string) error {
ug, err := c.mgr.List(ctx, model.UserGroup{ID: id})
if err != nil {
return err
}
if len(ug) == 0 {
return errors.NotFoundError(nil).WithMessage("the user group with id %v is not found", id)
}
return c.mgr.UpdateName(ctx, id, groupName)
}
func (c *controller) Create(ctx context.Context, group model.UserGroup) (int, error) {
if group.GroupType == common.LDAPGroupType {
ldapGroup, err := auth.SearchGroup(group.LdapGroupDN)
if err == ldap.ErrNotFound || ldapGroup == nil {
return 0, errors.BadRequestError(nil).WithMessage("LDAP Group DN is not found: DN:%v", group.LdapGroupDN)
}
if err == ldap.ErrDNSyntax {
return 0, errors.BadRequestError(nil).WithMessage("invalid DN syntax. DN: %v", group.LdapGroupDN)
}
if err != nil {
return 0, err
}
}
id, err := c.mgr.Create(ctx, group)
if err != nil && err == usergroup.ErrDupUserGroup {
return 0, errors.ConflictError(nil).
WithMessage("duplicate user group, group name:%v, group type: %v, ldap group DN: %v",
group.GroupName, group.GroupType, group.LdapGroupDN)
}
return id, err
}
func (c *controller) Get(ctx context.Context, id int) (*model.UserGroup, error) {
return c.mgr.Get(ctx, id)
}

View File

@ -0,0 +1,108 @@
// 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 test
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/usergroup"
_ "github.com/goharbor/harbor/src/core/auth/ldap"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
type controllerTestSuite struct {
htesting.Suite
controller usergroup.Controller
}
func (c *controllerTestSuite) SetupTest() {
c.controller = usergroup.Ctl
c.Suite.ClearTables = []string{"user_group"}
}
var defaultConfigWithVerifyCert = map[string]interface{}{
common.ExtEndpoint: "https://host01.com",
common.AUTHMode: common.LDAPAuth,
common.DatabaseType: "postgresql",
common.PostGreSQLHOST: "127.0.0.1",
common.PostGreSQLPort: 5432,
common.PostGreSQLUsername: "postgres",
common.PostGreSQLPassword: "root123",
common.PostGreSQLDatabase: "registry",
common.SelfRegistration: true,
common.LDAPURL: "ldap://127.0.0.1:389",
common.LDAPSearchDN: "cn=admin,dc=example,dc=com",
common.LDAPSearchPwd: "admin",
common.LDAPBaseDN: "dc=example,dc=com",
common.LDAPUID: "uid",
common.LDAPFilter: "",
common.LDAPScope: 2,
common.LDAPTimeout: 30,
common.LDAPVerifyCert: false,
common.LDAPGroupBaseDN: "ou=groups,dc=example,dc=com",
common.LDAPGroupSearchScope: 2,
common.LDAPGroupSearchFilter: "objectclass=groupOfNames",
common.LDAPGroupAttributeName: "cn",
common.TokenServiceURL: "http://token_service",
common.RegistryURL: "http://registry",
common.EmailHost: "127.0.0.1",
common.EmailPort: 25,
common.EmailUsername: "user01",
common.EmailPassword: "password",
common.EmailFrom: "from",
common.EmailSSL: true,
common.EmailIdentity: "",
common.ProjectCreationRestriction: common.ProCrtRestrAdmOnly,
common.MaxJobWorkers: 3,
common.TokenExpiration: 30,
common.AdminInitialPassword: "password",
common.WithNotary: false,
}
func (c *controllerTestSuite) TestCRUDUserGroup() {
config.InitWithSettings(defaultConfigWithVerifyCert)
ctx := c.Context()
ug := model.UserGroup{
GroupName: "harbor_dev",
GroupType: 1,
LdapGroupDN: "cn=harbor_dev,ou=groups,dc=example,dc=com",
}
id, err := c.controller.Create(ctx, ug)
c.Nil(err)
c.True(id > 0)
ug2, err2 := c.controller.Get(ctx, id)
c.Nil(err2)
c.Equal(ug2.GroupName, "harbor_dev")
c.Equal(ug2.GroupType, 1)
c.Equal(ug2.LdapGroupDN, "cn=harbor_dev,ou=groups,dc=example,dc=com")
err3 := c.controller.Update(ctx, id, "my_harbor_dev")
c.Nil(err3)
ug4, err4 := c.controller.Get(ctx, id)
c.Nil(err4)
c.Equal(ug4.GroupName, "my_harbor_dev")
err5 := c.controller.Delete(ctx, id)
c.Nil(err5)
}
func TestControllerTestSuite(t *testing.T) {
suite.Run(t, &controllerTestSuite{})
}

View File

@ -106,7 +106,6 @@ func init() {
beego.Router("/api/projects/:pid([0-9]+)/members/?:pmid([0-9]+)", &ProjectMemberAPI{})
beego.Router("/api/statistics", &StatisticAPI{})
beego.Router("/api/users/?:id", &UserAPI{})
beego.Router("/api/usergroups/?:ugid([0-9]+)", &UserGroupAPI{})
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
beego.Router("/api/labels", &LabelAPI{}, "post:Post;get:List")
beego.Router("/api/labels/:id([0-9]+", &LabelAPI{}, "get:Get;put:Put;delete:Delete")

View File

@ -16,15 +16,17 @@ package api
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"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/group"
"github.com/goharbor/harbor/src/common/dao/project"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac"
@ -252,7 +254,7 @@ func AddProjectMember(projectID int64, request models.MemberReq) (int, error) {
}
member.EntityID = groupID
} else if len(request.MemberGroup.GroupName) > 0 && request.MemberGroup.GroupType == common.HTTPGroupType || request.MemberGroup.GroupType == common.OIDCGroupType {
ugs, err := group.QueryUserGroup(models.UserGroup{GroupName: request.MemberGroup.GroupName, GroupType: request.MemberGroup.GroupType})
ugs, err := usergroup.Mgr.List(orm.Context(), model.UserGroup{GroupName: request.MemberGroup.GroupName, GroupType: request.MemberGroup.GroupType})
if err != nil {
return 0, err
}

View File

@ -16,11 +16,13 @@ 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/group"
"github.com/goharbor/harbor/src/common/dao/project"
"github.com/goharbor/harbor/src/common/models"
)
@ -95,14 +97,14 @@ func TestProjectMemberAPI_Post(t *testing.T) {
t.Errorf("Error occurred when create user: %v", err)
}
ugList, err := group.QueryUserGroup(models.UserGroup{GroupType: 1, LdapGroupDN: "cn=harbor_users,ou=sample,ou=vmware,dc=harbor,dc=com"})
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 := group.QueryUserGroup(models.UserGroup{GroupType: 2, GroupName: "vsphere.local\\administrators"})
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")
}
@ -190,7 +192,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
credential: admin,
bodyJSON: &models.MemberReq{
Role: 1,
MemberGroup: models.UserGroup{
MemberGroup: model.UserGroup{
GroupType: 1,
LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com",
},
@ -205,7 +207,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
credential: admin,
bodyJSON: &models.MemberReq{
Role: 1,
MemberGroup: models.UserGroup{
MemberGroup: model.UserGroup{
GroupType: 2,
ID: httpUgList[0].ID,
},
@ -220,7 +222,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
credential: admin,
bodyJSON: &models.MemberReq{
Role: 1,
MemberGroup: models.UserGroup{
MemberGroup: model.UserGroup{
GroupType: 1,
ID: ugList[0].ID,
},
@ -235,7 +237,7 @@ func TestProjectMemberAPI_Post(t *testing.T) {
credential: admin,
bodyJSON: &models.MemberReq{
Role: 1,
MemberGroup: models.UserGroup{
MemberGroup: model.UserGroup{
GroupType: 2,
GroupName: "vsphere.local/users",
},

View File

@ -1,204 +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 (
"errors"
"fmt"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/rbac/system"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"strconv"
"strings"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/ldap"
)
// UserGroupAPI ...
type UserGroupAPI struct {
BaseController
id int
groupType int
}
const (
userNameEmptyMsg = "User group name can not be empty!"
)
// Prepare validates the URL and parms
func (uga *UserGroupAPI) Prepare() {
uga.BaseController.Prepare()
if !uga.SecurityCtx.IsAuthenticated() {
uga.SendUnAuthorizedError(errors.New("Unauthorized"))
return
}
ugid, err := uga.GetInt64FromPath(":ugid")
if err != nil {
log.Warningf("failed to parse user group id, error: %v", err)
}
if ugid <= 0 && (uga.Ctx.Input.IsPut() || uga.Ctx.Input.IsDelete()) {
uga.SendBadRequestError(fmt.Errorf("invalid user group ID: %s", uga.GetStringFromPath(":ugid")))
return
}
uga.id = int(ugid)
authMode, err := config.AuthMode(orm.Context())
if err != nil {
uga.SendInternalServerError(errors.New("failed to get authentication mode"))
}
if authMode == common.LDAPAuth {
uga.groupType = common.LDAPGroupType
} else if authMode == common.HTTPAuth {
uga.groupType = common.HTTPGroupType
}
}
// Get ...
func (uga *UserGroupAPI) Get() {
ID := uga.id
uga.Data["json"] = make([]models.UserGroup, 0)
if ID == 0 {
// user group id not set, return all user group
query := models.UserGroup{GroupType: uga.groupType}
userGroupList, err := group.QueryUserGroup(query)
if err != nil {
uga.SendInternalServerError(fmt.Errorf("failed to query database for user group list, error: %v", err))
return
}
if len(userGroupList) > 0 {
uga.Data["json"] = userGroupList
}
} else {
// return a specific user group
userGroup, err := group.GetUserGroup(ID)
if userGroup == nil {
uga.SendNotFoundError(errors.New("the user group does not exist"))
return
}
if err != nil {
uga.SendInternalServerError(fmt.Errorf("failed to query database for user group list, error: %v", err))
return
}
uga.Data["json"] = userGroup
}
uga.ServeJSON()
}
// Post ... Create User Group
func (uga *UserGroupAPI) Post() {
userGroup := models.UserGroup{}
if err := uga.DecodeJSONReq(&userGroup); err != nil {
uga.SendBadRequestError(err)
return
}
userGroup.ID = 0
if userGroup.GroupType == 0 {
userGroup.GroupType = uga.groupType
}
userGroup.LdapGroupDN = strings.TrimSpace(userGroup.LdapGroupDN)
userGroup.GroupName = strings.TrimSpace(userGroup.GroupName)
if len(userGroup.GroupName) == 0 {
uga.SendBadRequestError(errors.New(userNameEmptyMsg))
return
}
if userGroup.GroupType == common.LDAPGroupType {
query := models.UserGroup{GroupType: userGroup.GroupType, LdapGroupDN: userGroup.LdapGroupDN}
result, err := group.QueryUserGroup(query)
if err != nil {
uga.SendInternalServerError(fmt.Errorf("error occurred in add user group, error: %v", err))
return
}
if len(result) > 0 {
uga.SendConflictError(errors.New("error occurred in add user group, duplicate user group exist"))
return
}
// User can not add ldap group when the ldap server is offline
ldapGroup, err := auth.SearchGroup(userGroup.LdapGroupDN)
if err == ldap.ErrNotFound || ldapGroup == nil {
uga.SendBadRequestError(fmt.Errorf("LDAP Group DN is not found: DN:%v", userGroup.LdapGroupDN))
return
}
if err == ldap.ErrDNSyntax {
uga.SendBadRequestError(fmt.Errorf("invalid DN syntax. DN: %v", userGroup.LdapGroupDN))
return
}
if err != nil {
uga.SendInternalServerError(fmt.Errorf("error occurred in search user group. error: %v", err))
return
}
}
groupID, err := group.AddUserGroup(userGroup)
if err != nil {
if err == group.ErrGroupNameDup {
uga.SendConflictError(fmt.Errorf("duplicated user group name %s", userGroup.GroupName))
return
}
uga.SendInternalServerError(fmt.Errorf("error occurred in add user group, error: %v", err))
return
}
uga.Redirect(http.StatusCreated, strconv.FormatInt(int64(groupID), 10))
}
// Put ... Only support update name
func (uga *UserGroupAPI) Put() {
userGroup := models.UserGroup{}
if err := uga.DecodeJSONReq(&userGroup); err != nil {
uga.SendBadRequestError(err)
return
}
if userGroup.GroupType == common.HTTPGroupType {
uga.SendBadRequestError(errors.New("HTTP group is not allowed to update"))
return
}
ID := uga.id
userGroup.GroupName = strings.TrimSpace(userGroup.GroupName)
if len(userGroup.GroupName) == 0 {
uga.SendBadRequestError(errors.New(userNameEmptyMsg))
return
}
userGroup.GroupType = common.LDAPGroupType
log.Debugf("Updated user group %v", userGroup)
err := group.UpdateUserGroupName(ID, userGroup.GroupName)
if err != nil {
uga.SendInternalServerError(fmt.Errorf("Error occurred in update user group, error: %v", err))
return
}
return
}
// Delete ...
func (uga *UserGroupAPI) Delete() {
resource := system.NewNamespace().Resource(rbac.ResourceUserGroup)
if !uga.SecurityCtx.Can(uga.Context(), rbac.ActionDelete, resource) {
uga.SendForbiddenError(errors.New(uga.SecurityCtx.GetUsername()))
return
}
err := group.DeleteUserGroup(uga.id)
if err != nil {
uga.SendInternalServerError(fmt.Errorf("Error occurred in update user group, error: %v", err))
return
}
return
}

View File

@ -1,192 +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"
"testing"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
)
const (
URL = "/api/usergroups"
)
func TestUserGroupAPI_GetAndDelete(t *testing.T) {
groupID, err := group.AddUserGroup(models.UserGroup{
GroupName: "harbor_users",
LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com",
GroupType: common.LDAPGroupType,
})
if err != nil {
t.Errorf("Error occurred when AddUserGroup: %v", err)
}
defer group.DeleteUserGroup(groupID)
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
method: http.MethodGet,
url: URL,
},
code: http.StatusUnauthorized,
},
// 200
{
request: &testingRequest{
method: http.MethodGet,
url: fmt.Sprintf("/api/usergroups/%d", groupID),
credential: admin,
},
code: http.StatusOK,
},
// 200
{
request: &testingRequest{
method: http.MethodGet,
url: fmt.Sprintf("/api/usergroups"),
credential: admin,
},
code: http.StatusOK,
},
// 200
{
request: &testingRequest{
method: http.MethodDelete,
url: fmt.Sprintf("/api/usergroups/%d", groupID),
credential: admin,
},
code: http.StatusOK,
},
}
runCodeCheckingCases(t, cases...)
}
func TestUserGroupAPI_Post(t *testing.T) {
groupID, err := group.AddUserGroup(models.UserGroup{
GroupName: "harbor_group",
LdapGroupDN: "cn=harbor_group,ou=groups,dc=example,dc=com",
GroupType: common.LDAPGroupType,
})
if err != nil {
t.Errorf("Error occurred when AddUserGroup: %v", err)
}
defer group.DeleteUserGroup(groupID)
cases := []*codeCheckingCase{
// 409
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/usergroups",
bodyJSON: &models.UserGroup{
GroupName: "harbor_group",
LdapGroupDN: "cn=harbor_group,ou=groups,dc=example,dc=com",
GroupType: common.LDAPGroupType,
},
credential: admin,
},
code: http.StatusConflict,
},
// 201
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/usergroups",
bodyJSON: &models.UserGroup{
GroupName: "vsphere.local\\guest",
GroupType: common.HTTPGroupType,
},
credential: admin,
},
code: http.StatusCreated,
},
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/usergroups",
bodyJSON: &models.UserGroup{
GroupName: "vsphere.local\\guest",
GroupType: common.HTTPGroupType,
},
credential: admin,
},
code: http.StatusConflict,
},
}
runCodeCheckingCases(t, cases...)
}
func TestUserGroupAPI_Put(t *testing.T) {
groupID, err := group.AddUserGroup(models.UserGroup{
GroupName: "harbor_group",
LdapGroupDN: "cn=harbor_groups,ou=groups,dc=example,dc=com",
GroupType: common.LDAPGroupType,
})
defer group.DeleteUserGroup(groupID)
if err != nil {
t.Errorf("Error occurred when AddUserGroup: %v", err)
}
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
method: http.MethodPut,
url: fmt.Sprintf("/api/usergroups/%d", groupID),
bodyJSON: &models.UserGroup{
GroupName: "my_group",
},
},
code: http.StatusUnauthorized,
},
// 200
{
request: &testingRequest{
method: http.MethodPut,
url: fmt.Sprintf("/api/usergroups/%d", groupID),
bodyJSON: &models.UserGroup{
GroupName: "my_group",
},
credential: admin,
},
code: http.StatusOK,
},
// 400
{
request: &testingRequest{
method: http.MethodPut,
url: fmt.Sprintf("/api/usergroups/%d", groupID),
bodyJSON: &models.UserGroup{
GroupName: "my_group",
GroupType: common.HTTPGroupType,
},
credential: admin,
},
code: http.StatusBadRequest,
},
}
runCodeCheckingCases(t, cases...)
}

View File

@ -14,6 +14,7 @@
package auth
import (
"github.com/goharbor/harbor/src/pkg/usergroup/model"
"testing"
"time"
@ -71,7 +72,7 @@ func TestDefaultMethods(t *testing.T) {
t.Fatal("Default implementation should return error")
}
err = authHelper.OnBoardGroup(&models.UserGroup{}, "sample")
err = authHelper.OnBoardGroup(&model.UserGroup{}, "sample")
if err == nil {
t.Fatal("Default implementation should return error")
}

View File

@ -17,9 +17,11 @@ package auth
import (
"errors"
"fmt"
"time"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"time"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
@ -70,12 +72,12 @@ type AuthenticateHelper interface {
// on the data record of the user
OnBoardUser(u *models.User) error
// Create a group in harbor DB, if altGroupName is not empty, take the altGroupName as groupName in harbor DB.
OnBoardGroup(g *models.UserGroup, altGroupName string) error
OnBoardGroup(g *model.UserGroup, altGroupName string) error
// Get user information from account repository
SearchUser(username string) (*models.User, error)
// Search a group based on specific authentication
SearchGroup(groupDN string) (*models.UserGroup, error)
// Update user information after authenticate, such as OnBoard or sync info etc
SearchGroup(groupDN string) (*model.UserGroup, error)
// Update user information after authenticate, such as Onboard or sync info etc
PostAuthenticate(u *models.User) error
}
@ -106,12 +108,12 @@ func (d *DefaultAuthenticateHelper) PostAuthenticate(u *models.User) error {
}
// OnBoardGroup - OnBoardGroup, it will set the ID of the user group, if altGroupName is not empty, take the altGroupName as groupName in harbor DB.
func (d *DefaultAuthenticateHelper) OnBoardGroup(u *models.UserGroup, altGroupName string) error {
func (d *DefaultAuthenticateHelper) OnBoardGroup(u *model.UserGroup, altGroupName string) error {
return errors.New("Not supported")
}
// SearchGroup - Search ldap group by group key, groupKey is the unique attribute of group in authenticator, for LDAP, the key is group DN
func (d *DefaultAuthenticateHelper) SearchGroup(groupKey string) (*models.UserGroup, error) {
func (d *DefaultAuthenticateHelper) SearchGroup(groupKey string) (*model.UserGroup, error) {
return nil, errors.New("Not supported")
}
@ -193,7 +195,7 @@ func SearchUser(username string) (*models.User, error) {
}
// OnBoardGroup - Create a user group in harbor db, if altGroupName is not empty, take the altGroupName as groupName in harbor DB
func OnBoardGroup(userGroup *models.UserGroup, altGroupName string) error {
func OnBoardGroup(userGroup *model.UserGroup, altGroupName string) error {
helper, err := getHelper()
if err != nil {
return err
@ -202,7 +204,7 @@ func OnBoardGroup(userGroup *models.UserGroup, altGroupName string) error {
}
// SearchGroup -- Search group in authenticator, groupKey is the unique attribute of group in authenticator, for LDAP, the key is group DN
func SearchGroup(groupKey string) (*models.UserGroup, error) {
func SearchGroup(groupKey string) (*model.UserGroup, error) {
helper, err := getHelper()
if err != nil {
return nil, err

View File

@ -20,21 +20,22 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/jobservice/logger"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"io/ioutil"
"net/http"
"strings"
"sync"
"time"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/jobservice/logger"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"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/models"
"github.com/goharbor/harbor/src/controller/usergroup"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/authproxy"
@ -168,14 +169,14 @@ func (a *Auth) SearchUser(username string) (*models.User, error) {
}
// SearchGroup search group exist in the authentication provider, for HTTP auth, if SkipSearch is true, it assume this group exist in authentication provider.
func (a *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
func (a *Auth) SearchGroup(groupKey string) (*model.UserGroup, error) {
err := a.ensure()
if err != nil {
log.Warningf("Failed to refresh configuration for HTTP Auth Proxy Authenticator, error: %v, the default settings will be used", err)
}
var ug *models.UserGroup
var ug *model.UserGroup
if a.SkipSearch {
ug = &models.UserGroup{
ug = &model.UserGroup{
GroupName: groupKey,
GroupType: common.HTTPGroupType,
}
@ -185,13 +186,13 @@ func (a *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
}
// OnBoardGroup create user group entity in Harbor DB, altGroupName is not used.
func (a *Auth) OnBoardGroup(u *models.UserGroup, altGroupName string) error {
func (a *Auth) OnBoardGroup(u *model.UserGroup, altGroupName string) error {
// if group name provided, on board the user group
if len(u.GroupName) == 0 {
return errors.New("Should provide a group name")
}
u.GroupType = common.HTTPGroupType
err := group.OnBoardUserGroup(u)
err := usergroup.Ctl.Ensure(orm.Context(), u)
if err != nil {
return err
}

View File

@ -17,13 +17,15 @@ package authproxy
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
cut "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/auth/authproxy/test"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"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"
"net/http/httptest"
"os"
@ -71,7 +73,7 @@ func TestMain(m *testing.M) {
}
func TestAuth_Authenticate(t *testing.T) {
userGroups := []models.UserGroup{
userGroups := []model.UserGroup{
{GroupName: "vsphere.local\\users", GroupType: common.HTTPGroupType},
{GroupName: "vsphere.local\\administrators", GroupType: common.HTTPGroupType},
{GroupName: "vsphere.local\\caadmins", GroupType: common.HTTPGroupType},
@ -80,8 +82,7 @@ func TestAuth_Authenticate(t *testing.T) {
{GroupName: "vsphere.local\\licenseservice.administrators", GroupType: common.HTTPGroupType},
{GroupName: "vsphere.local\\everyone", GroupType: common.HTTPGroupType},
}
groupIDs, err := group.PopulateGroup(userGroups)
groupIDs, err := usergroup.Mgr.Populate(orm.Context(), userGroups)
if err != nil {
t.Fatal("Failed to get groupIDs")
}
@ -190,18 +191,18 @@ func TestAuth_PostAuthenticate(t *testing.T) {
}
func TestAuth_OnBoardGroup(t *testing.T) {
input := &models.UserGroup{
input := &model.UserGroup{
GroupName: "OnBoardTest",
GroupType: common.HTTPGroupType,
}
a.OnBoardGroup(input, "")
assert.True(t, input.ID > 0, "The OnBoardGroup should have a valid group ID")
g, er := group.GetUserGroup(input.ID)
g, er := usergroup.Mgr.Get(orm.Context(), input.ID)
assert.Nil(t, er)
assert.Equal(t, "OnBoardTest", g.GroupName)
emptyGroup := &models.UserGroup{}
emptyGroup := &model.UserGroup{}
err := a.OnBoardGroup(emptyGroup, "")
if err == nil {
t.Fatal("Empty user group should failed to OnBoard")

View File

@ -17,20 +17,22 @@ package ldap
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/ldap/model"
"regexp"
"strings"
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
goldap "github.com/go-ldap/ldap/v3"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/models"
ldapCtl "github.com/goharbor/harbor/src/controller/ldap"
ugCtl "github.com/goharbor/harbor/src/controller/usergroup"
"github.com/goharbor/harbor/src/pkg/ldap"
"github.com/goharbor/harbor/src/core/auth"
@ -113,7 +115,7 @@ func (l *Auth) attachLDAPGroup(ctx context.Context, ldapUsers []model.User, u *m
}
}
userGroups := make([]models.UserGroup, 0)
userGroups := make([]ugModel.UserGroup, 0)
for _, dn := range ldapUsers[0].GroupDNList {
lGroups, err := sess.SearchGroupByDN(dn)
if err != nil {
@ -124,9 +126,9 @@ func (l *Auth) attachLDAPGroup(ctx context.Context, ldapUsers []model.User, u *m
log.Warningf("Can not get the ldap group name with DN %v", dn)
continue
}
userGroups = append(userGroups, models.UserGroup{GroupName: lGroups[0].Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lGroups[0].Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
}
u.GroupIDs, err = group.PopulateGroup(userGroups)
u.GroupIDs, err = ugCtl.Ctl.Populate(orm.Context(), userGroups)
if err != nil {
log.Warningf("Failed to fetch ldap group configuration:%v", err)
}
@ -167,12 +169,12 @@ func (l *Auth) SearchUser(username string) (*models.User, error) {
return nil, err
}
if err = s.Open(); err != nil {
return nil, fmt.Errorf("Failed to load system ldap config, %v", err)
return nil, fmt.Errorf("failed to load system ldap config, %v", err)
}
defer s.Close()
lUsers, err := s.SearchUser(username)
if err != nil {
return nil, fmt.Errorf("Failed to search user in ldap")
return nil, fmt.Errorf("failed to search user in ldap")
}
if len(lUsers) > 1 {
@ -186,14 +188,14 @@ func (l *Auth) SearchUser(username string) (*models.User, error) {
log.Debugf("Found ldap user %v", user)
} else {
return nil, fmt.Errorf("No user found, %v", username)
return nil, fmt.Errorf("no user found, %v", username)
}
return &user, nil
}
// SearchGroup -- Search group in ldap authenticator, groupKey is LDAP group DN.
func (l *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
func (l *Auth) SearchGroup(groupKey string) (*ugModel.UserGroup, error) {
if _, err := goldap.ParseDN(groupKey); err != nil {
return nil, auth.ErrInvalidLDAPGroupDN
}
@ -216,9 +218,9 @@ func (l *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
}
if len(userGroupList) == 0 {
return nil, fmt.Errorf("Failed to searh ldap group with groupDN:%v", groupKey)
return nil, fmt.Errorf("failed to searh ldap group with groupDN:%v", groupKey)
}
userGroup := models.UserGroup{
userGroup := ugModel.UserGroup{
GroupName: userGroupList[0].Name,
LdapGroupDN: userGroupList[0].Dn,
}
@ -226,7 +228,8 @@ func (l *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
}
// OnBoardGroup -- Create Group in harbor DB, if altGroupName is not empty, take the altGroupName as groupName in harbor DB.
func (l *Auth) OnBoardGroup(u *models.UserGroup, altGroupName string) error {
func (l *Auth) OnBoardGroup(u *ugModel.UserGroup, altGroupName string) error {
ctx := orm.Context()
if _, err := goldap.ParseDN(u.LdapGroupDN); err != nil {
return auth.ErrInvalidLDAPGroupDN
}
@ -235,14 +238,14 @@ func (l *Auth) OnBoardGroup(u *models.UserGroup, altGroupName string) error {
}
u.GroupType = common.LDAPGroupType
// Check duplicate LDAP DN in usergroup, if usergroup exist, return error
userGroupList, err := group.QueryUserGroup(models.UserGroup{LdapGroupDN: u.LdapGroupDN})
userGroupList, err := ugCtl.Ctl.List(ctx, ugModel.UserGroup{LdapGroupDN: u.LdapGroupDN})
if err != nil {
return err
}
if len(userGroupList) > 0 {
return auth.ErrDuplicateLDAPGroup
}
return group.OnBoardUserGroup(u)
return ugCtl.Ctl.Ensure(ctx, u)
}
// PostAuthenticate -- If user exist in harbor DB, sync email address, if not exist, call OnBoardUser
@ -287,7 +290,7 @@ func (l *Auth) PostAuthenticate(u *models.User) error {
return err
}
if u.UserID <= 0 {
return fmt.Errorf("Can not OnBoardUser %v", u)
return fmt.Errorf("cannot OnBoardUser %v", u)
}
return nil
}

View File

@ -14,13 +14,15 @@
package ldap
import (
"github.com/goharbor/harbor/src/controller/config"
"github.com/stretchr/testify/assert"
// "fmt"
// "strings"
"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/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/dao/project"
@ -29,7 +31,6 @@ import (
"github.com/goharbor/harbor/src/core/api"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/core/auth"
)
@ -266,7 +267,7 @@ func TestAuthenticateHelperOnBoardUser(t *testing.T) {
}
func TestOnBoardGroup(t *testing.T) {
group := models.UserGroup{
group := model.UserGroup{
GroupName: "harbor_group2",
LdapGroupDN: "cn=harbor_group2,ou=groups,dc=example,dc=com",
}
@ -402,11 +403,11 @@ func TestAddProjectMemberWithLdapGroup(t *testing.T) {
if err != nil {
t.Errorf("Error occurred when GetProjectByName: %v", err)
}
userGroups := []models.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 := group.PopulateGroup(userGroups)
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}}
groupIds, err := usergroup.Mgr.Populate(orm.Context(), userGroups)
member := models.MemberReq{
ProjectID: currentProject.ProjectID,
MemberGroup: models.UserGroup{
MemberGroup: model.UserGroup{
ID: groupIds[0],
},
Role: common.RoleProjectAdmin,

View File

@ -3,9 +3,10 @@ package oidc
import (
"fmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/usergroup"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
)
// Auth of OIDC mode only implements the funcs for onboarding group
@ -14,20 +15,20 @@ type Auth struct {
}
// SearchGroup is skipped in OIDC mode, so it makes sure any group will be onboarded.
func (a *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
return &models.UserGroup{
func (a *Auth) SearchGroup(groupKey string) (*model.UserGroup, error) {
return &model.UserGroup{
GroupName: groupKey,
GroupType: common.OIDCGroupType,
}, nil
}
// OnBoardGroup create user group entity in Harbor DB, altGroupName is not used.
func (a *Auth) OnBoardGroup(u *models.UserGroup, altGroupName string) error {
func (a *Auth) OnBoardGroup(u *model.UserGroup, altGroupName string) error {
// if group name provided, on board the user group
if len(u.GroupName) == 0 || u.GroupType != common.OIDCGroupType {
return fmt.Errorf("invalid input group for OIDC mode: %v", *u)
}
return group.OnBoardUserGroup(u)
return usergroup.Mgr.Onboard(orm.Context(), u)
}
func init() {

View File

@ -2,7 +2,7 @@ package oidc
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
"github.com/stretchr/testify/assert"
"os"
"testing"
@ -17,15 +17,15 @@ func TestAuth_SearchGroup(t *testing.T) {
a := Auth{}
res, err := a.SearchGroup("grp")
assert.Nil(t, err)
assert.Equal(t, models.UserGroup{GroupName: "grp", GroupType: common.OIDCGroupType}, *res)
assert.Equal(t, model.UserGroup{GroupName: "grp", GroupType: common.OIDCGroupType}, *res)
}
func TestAuth_OnBoardGroup(t *testing.T) {
a := Auth{}
g1 := &models.UserGroup{GroupName: "", GroupType: common.OIDCGroupType}
g1 := &model.UserGroup{GroupName: "", GroupType: common.OIDCGroupType}
err1 := a.OnBoardGroup(g1, "")
assert.NotNil(t, err1)
g2 := &models.UserGroup{GroupName: "group", GroupType: common.LDAPGroupType}
g2 := &model.UserGroup{GroupName: "group", GroupType: common.LDAPGroupType}
err2 := a.OnBoardGroup(g2, "")
assert.NotNil(t, err2)
}

View File

@ -4,10 +4,12 @@ import (
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/usergroup"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -95,8 +97,8 @@ func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus, adminGroups
}
if len(status.User.Groups) > 0 {
userGroups := models.UserGroupsFromName(status.User.Groups, common.HTTPGroupType)
groupIDList, err := group.PopulateGroup(userGroups)
userGroups := model.UserGroupsFromName(status.User.Groups, common.HTTPGroupType)
groupIDList, err := usergroup.Mgr.Populate(orm.Context(), userGroups)
if err != nil {
return nil, err
}

View File

@ -1,14 +1,16 @@
package authproxy
import (
"os"
"testing"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/usergroup"
"github.com/stretchr/testify/assert"
"k8s.io/api/authentication/v1beta1"
"k8s.io/client-go/rest"
"os"
"testing"
)
func TestMain(m *testing.M) {
@ -110,7 +112,7 @@ func TestUserFromReviewStatus(t *testing.T) {
if u != nil {
for _, gid := range u.GroupIDs {
t.Logf("Deleting group %d", gid)
if err := group.DeleteUserGroup(gid); err != nil {
if err := usergroup.Mgr.Delete(orm.Context(), gid); err != nil {
panic(err)
}
}

View File

@ -19,18 +19,20 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/goharbor/harbor/src/controller/config"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/usergroup"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
gooidc "github.com/coreos/go-oidc"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/log"
"golang.org/x/oauth2"
@ -387,7 +389,7 @@ func groupsFromClaims(gp claimsProvider, k string) ([]string, bool) {
type populate func(groupNames []string) ([]int, error)
func populateGroupsDB(groupNames []string) ([]int, error) {
return group.PopulateGroup(models.UserGroupsFromName(groupNames, common.OIDCGroupType))
return usergroup.Mgr.Populate(orm.Context(), model.UserGroupsFromName(groupNames, common.OIDCGroupType))
}
// InjectGroupsToUser populates the group to DB and inject the group IDs to user model.

View File

@ -0,0 +1,194 @@
// 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 (
"context"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
"time"
)
func init() {
orm.RegisterModel(
new(model.UserGroup),
)
}
// DAO the dao for user group
type DAO interface {
// Add add user group
Add(ctx context.Context, userGroup model.UserGroup) (int, error)
// Query query user group
Query(ctx context.Context, query model.UserGroup) ([]*model.UserGroup, error)
// Get get user group by id
Get(ctx context.Context, id int) (*model.UserGroup, error)
// Delete delete user group by id
Delete(ctx context.Context, id int) error
// UpdateName update user group name
UpdateName(ctx context.Context, id int, groupName string) error
// ReadOrCreate create a user group or read existing one from db
ReadOrCreate(ctx context.Context, g *model.UserGroup, keyAttribute string, combinedKeyAttributes ...string) (bool, int64, error)
}
type dao struct {
}
// New create user group DAO
func New() DAO {
return &dao{}
}
// ErrGroupNameDup ...
var ErrGroupNameDup = errors.New("duplicated user group name")
// Add - Add User Group
func (d *dao) Add(ctx context.Context, userGroup model.UserGroup) (int, error) {
userGroupList, err := d.Query(ctx, model.UserGroup{GroupName: userGroup.GroupName, GroupType: common.HTTPGroupType})
if err != nil {
return 0, ErrGroupNameDup
}
if len(userGroupList) > 0 {
return 0, ErrGroupNameDup
}
o, err := orm.FromContext(ctx)
if err != nil {
return 0, err
}
sql := "insert into user_group (group_name, group_type, ldap_group_dn, creation_time, update_time) values (?, ?, ?, ?, ?) RETURNING id"
var id int
now := time.Now()
err = o.Raw(sql, userGroup.GroupName, userGroup.GroupType, utils.TrimLower(userGroup.LdapGroupDN), now, now).QueryRow(&id)
if err != nil {
return 0, err
}
return id, nil
}
// Query - Query User Group
func (d *dao) Query(ctx context.Context, query model.UserGroup) ([]*model.UserGroup, error) {
o, err := orm.FromContext(ctx)
if err != nil {
return nil, err
}
sql := `select id, group_name, group_type, ldap_group_dn from user_group where 1=1 `
sqlParam := make([]interface{}, 1)
var groups []*model.UserGroup
if len(query.GroupName) != 0 {
sql += ` and group_name = ? `
sqlParam = append(sqlParam, query.GroupName)
}
if query.GroupType != 0 {
sql += ` and group_type = ? `
sqlParam = append(sqlParam, query.GroupType)
}
if len(query.LdapGroupDN) != 0 {
sql += ` and ldap_group_dn = ? `
sqlParam = append(sqlParam, utils.TrimLower(query.LdapGroupDN))
}
if query.ID != 0 {
sql += ` and id = ? `
sqlParam = append(sqlParam, query.ID)
}
_, err = o.Raw(sql, sqlParam).QueryRows(&groups)
if err != nil {
return nil, err
}
return groups, nil
}
// Get ...
func (d *dao) Get(ctx context.Context, id int) (*model.UserGroup, error) {
userGroup := model.UserGroup{ID: id}
userGroupList, err := d.Query(ctx, userGroup)
if err != nil {
return nil, err
}
if len(userGroupList) > 0 {
return userGroupList[0], nil
}
return nil, nil
}
// Delete ...
func (d *dao) Delete(ctx context.Context, id int) error {
userGroup := model.UserGroup{ID: id}
o, err := orm.FromContext(ctx)
if err != nil {
return err
}
_, err = o.Delete(&userGroup)
if err == nil {
// Delete all related project members
sql := `delete from project_member where entity_id = ? and entity_type='g'`
_, err := o.Raw(sql, id).Exec()
if err != nil {
return err
}
}
return err
}
// UpdateName ...
func (d *dao) UpdateName(ctx context.Context, id int, groupName string) error {
log.Debugf("Updating user_group with id:%v, name:%v", id, groupName)
o, err := orm.FromContext(ctx)
if err != nil {
return err
}
sql := "update user_group set group_name = ? where id = ? "
_, err = o.Raw(sql, groupName, id).Exec()
return err
}
// ReadOrCreate read or create user group
func (d *dao) ReadOrCreate(ctx context.Context, g *model.UserGroup, keyAttribute string, combinedKeyAttributes ...string) (bool, int64, error) {
o, err := orm.FromContext(ctx)
if err != nil {
return false, 0, err
}
return o.ReadOrCreate(g, keyAttribute, combinedKeyAttributes...)
}
func (d *dao) onBoardCommonUserGroup(ctx context.Context, g *model.UserGroup, keyAttribute string, combinedKeyAttributes ...string) error {
g.LdapGroupDN = utils.TrimLower(g.LdapGroupDN)
created, ID, err := d.ReadOrCreate(ctx, g, keyAttribute, combinedKeyAttributes...)
if err != nil {
return err
}
if created {
g.ID = int(ID)
} else {
prevGroup, err := d.Get(ctx, int(ID))
if err != nil {
return err
}
g.ID = prevGroup.ID
g.GroupName = prevGroup.GroupName
g.GroupType = prevGroup.GroupType
g.LdapGroupDN = prevGroup.LdapGroupDN
}
return nil
}

View File

@ -0,0 +1,67 @@
// 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/pkg/usergroup/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
type DaoTestSuite struct {
htesting.Suite
dao DAO
}
func (s *DaoTestSuite) SetupSuite() {
s.Suite.SetupSuite()
s.Suite.ClearTables = []string{"user_group"}
s.dao = New()
}
func (s *DaoTestSuite) TestCRUDUsergroup() {
ctx := s.Context()
userGroup := model.UserGroup{
GroupName: "harbor_dev",
GroupType: 1,
LdapGroupDN: "cn=harbor_dev,ou=groups,dc=example,dc=com",
}
id, err := s.dao.Add(ctx, userGroup)
s.Nil(err)
s.True(id > 0)
ug, err2 := s.dao.Get(ctx, id)
s.Nil(err2)
s.Equal("harbor_dev", ug.GroupName)
s.Equal("cn=harbor_dev,ou=groups,dc=example,dc=com", ug.LdapGroupDN)
s.Equal(1, ug.GroupType)
err3 := s.dao.UpdateName(ctx, id, "my_harbor_dev")
s.Nil(err3)
ug2, err4 := s.dao.Get(ctx, id)
s.Nil(err4)
s.Equal("my_harbor_dev", ug2.GroupName)
s.Equal("cn=harbor_dev,ou=groups,dc=example,dc=com", ug2.LdapGroupDN)
s.Equal(1, ug2.GroupType)
err5 := s.dao.Delete(ctx, id)
s.Nil(err5)
}
func TestDaoTestSuite(t *testing.T) {
suite.Run(t, &DaoTestSuite{})
}

View File

@ -0,0 +1,129 @@
// 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 usergroup
import (
"context"
"errors"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/pkg/usergroup/dao"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
)
var (
// Mgr default user group manager
Mgr = newManager()
// ErrDupUserGroup duplicate user group error
ErrDupUserGroup = errors.New("duplicated user group name found")
)
// Manager interface provide the management functions for user group
type Manager interface {
// Create create user group
Create(ctx context.Context, userGroup model.UserGroup) (int, error)
// List list user group
List(ctx context.Context, query model.UserGroup) ([]*model.UserGroup, error)
// Get get user group by id
Get(ctx context.Context, id int) (*model.UserGroup, error)
// Populate populate user group from external auth server to Harbor and return the group id
Populate(ctx context.Context, userGroups []model.UserGroup) ([]int, error)
// Delete delete user group by id
Delete(ctx context.Context, id int) error
// UpdateName update user group's name
UpdateName(ctx context.Context, id int, groupName string) error
// Onboard sync the user group from external auth server to Harbor
Onboard(ctx context.Context, g *model.UserGroup) error
}
type manager struct {
dao dao.DAO
}
func newManager() Manager {
return &manager{dao: dao.New()}
}
func (m *manager) Create(ctx context.Context, userGroup model.UserGroup) (int, error) {
query := model.UserGroup{
GroupName: userGroup.GroupName,
GroupType: userGroup.GroupType,
}
ug, err := m.dao.Query(ctx, query)
if err != nil {
return 0, err
}
if len(ug) > 0 {
return 0, ErrDupUserGroup
}
return m.dao.Add(ctx, userGroup)
}
func (m *manager) List(ctx context.Context, query model.UserGroup) ([]*model.UserGroup, error) {
return m.dao.Query(ctx, query)
}
func (m *manager) Get(ctx context.Context, id int) (*model.UserGroup, error) {
return m.dao.Get(ctx, id)
}
func (m *manager) Populate(ctx context.Context, userGroups []model.UserGroup) ([]int, error) {
ugList := make([]int, 0)
for _, group := range userGroups {
err := m.Onboard(ctx, &group)
if err != nil {
return ugList, err
}
if group.ID > 0 {
ugList = append(ugList, group.ID)
}
}
return ugList, nil
}
func (m *manager) Delete(ctx context.Context, id int) error {
return m.dao.Delete(ctx, id)
}
func (m *manager) UpdateName(ctx context.Context, id int, groupName string) error {
return m.dao.UpdateName(ctx, id, groupName)
}
func (m *manager) Onboard(ctx context.Context, g *model.UserGroup) error {
if g.GroupType == common.LDAPGroupType {
return m.onBoardCommonUserGroup(ctx, g, "LdapGroupDN", "GroupType")
}
return m.onBoardCommonUserGroup(ctx, g, "GroupName", "GroupType")
}
func (m *manager) onBoardCommonUserGroup(ctx context.Context, g *model.UserGroup, keyAttribute string, combinedKeyAttributes ...string) error {
g.LdapGroupDN = utils.TrimLower(g.LdapGroupDN)
created, ID, err := m.dao.ReadOrCreate(ctx, g, keyAttribute, combinedKeyAttributes...)
if err != nil {
return err
}
if created {
g.ID = int(ID)
} else {
prevGroup, err := m.dao.Get(ctx, int(ID))
if err != nil {
return err
}
g.ID = prevGroup.ID
g.GroupName = prevGroup.GroupName
g.GroupType = prevGroup.GroupType
g.LdapGroupDN = prevGroup.LdapGroupDN
}
return nil
}

View File

@ -0,0 +1,73 @@
// 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 usergroup
import (
"github.com/goharbor/harbor/src/pkg/usergroup/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
type ManagerTestSuite struct {
htesting.Suite
mgr Manager
}
func (s *ManagerTestSuite) SetupSuite() {
s.Suite.SetupSuite()
s.Suite.ClearTables = []string{"user_group"}
s.mgr = newManager()
}
func (s *ManagerTestSuite) TestOnboardGroup() {
ctx := s.Context()
ug := &model.UserGroup{
GroupName: "harbor_dev",
GroupType: 1,
LdapGroupDN: "cn=harbor_dev,ou=groups,dc=example,dc=com",
}
err := s.mgr.Onboard(ctx, ug)
s.Nil(err)
qm := model.UserGroup{GroupType: 1, LdapGroupDN: "cn=harbor_dev,ou=groups,dc=example,dc=com"}
ugs, err := s.mgr.List(ctx, qm)
s.Nil(err)
s.True(len(ugs) > 0)
}
func (s *ManagerTestSuite) TestPopulateGroup() {
ctx := s.Context()
ugs := []model.UserGroup{
{
GroupName: "harbor_dev",
GroupType: 1,
LdapGroupDN: "cn=harbor_dev,ou=groups,dc=example,dc=com",
},
{
GroupName: "myhttp_group",
GroupType: 2,
},
}
ids, err := s.mgr.Populate(ctx, ugs)
s.Nil(err)
s.True(len(ids) > 0)
for _, i := range ids {
s.True(i > 0)
}
}
func TestManagerTestSuite(t *testing.T) {
suite.Run(t, &ManagerTestSuite{})
}

View File

@ -1,18 +1,18 @@
// Copyright Project Harbor Authors
// 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
// 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
// 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.
// 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
package model
// UserGroupTable is the name of table in DB that holds the user object
const UserGroupTable = "user_group"

View File

@ -57,6 +57,7 @@ func New() http.Handler {
OidcAPI: newOIDCAPI(),
SystemCVEAllowlistAPI: newSystemCVEAllowListAPI(),
ConfigureAPI: newConfigAPI(),
UsergroupAPI: newUserGroupAPI(),
})
if err != nil {
log.Fatal(err)

View File

@ -0,0 +1,150 @@
// 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"
"fmt"
"github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/controller/config"
ugCtl "github.com/goharbor/harbor/src/controller/usergroup"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/pkg/usergroup/model"
"github.com/goharbor/harbor/src/server/v2.0/models"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/usergroup"
"strings"
)
type userGroupAPI struct {
BaseAPI
ctl ugCtl.Controller
}
func newUserGroupAPI() *userGroupAPI {
return &userGroupAPI{ctl: ugCtl.Ctl}
}
func (u *userGroupAPI) CreateUserGroup(ctx context.Context, params operation.CreateUserGroupParams) middleware.Responder {
if err := u.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceUserGroup); err != nil {
return u.SendError(ctx, err)
}
if params.Usergroup == nil {
return operation.NewCreateUserGroupBadRequest()
}
if len(params.Usergroup.GroupName) == 0 {
return operation.NewCreateUserGroupBadRequest()
}
ug := model.UserGroup{
GroupName: params.Usergroup.GroupName,
GroupType: int(params.Usergroup.GroupType),
LdapGroupDN: params.Usergroup.LdapGroupDn,
}
id, err := u.ctl.Create(ctx, ug)
if err != nil {
return u.SendError(ctx, err)
}
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
return operation.NewCreateUserGroupCreated().WithLocation(location)
}
func (u *userGroupAPI) DeleteUserGroup(ctx context.Context, params operation.DeleteUserGroupParams) middleware.Responder {
if err := u.RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceUserGroup); err != nil {
return u.SendError(ctx, err)
}
if params.GroupID <= 0 {
return u.SendError(ctx, errors.BadRequestError(nil).WithMessage("the group id should be provided"))
}
err := u.ctl.Delete(ctx, int(params.GroupID))
if err != nil {
return u.SendError(ctx, err)
}
return operation.NewDeleteUserGroupOK()
}
func (u *userGroupAPI) GetUserGroup(ctx context.Context, params operation.GetUserGroupParams) middleware.Responder {
if err := u.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceUserGroup); err != nil {
return u.SendError(ctx, err)
}
if params.GroupID <= 0 {
return u.SendError(ctx, errors.BadRequestError(nil).WithMessage("the group id should be provided"))
}
ug, err := u.ctl.Get(ctx, int(params.GroupID))
if err != nil {
return u.SendError(ctx, err)
}
if ug == nil {
return u.SendError(ctx, errors.NotFoundError(nil).WithMessage("the user group with id %v is not found", params.GroupID))
}
userGroup := &models.UserGroup{
GroupName: ug.GroupName,
GroupType: int64(ug.GroupType),
LdapGroupDn: ug.LdapGroupDN,
}
return operation.NewGetUserGroupOK().WithPayload(userGroup)
}
func (u *userGroupAPI) ListUserGroups(ctx context.Context, params operation.ListUserGroupsParams) middleware.Responder {
if err := u.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceUserGroup); err != nil {
return u.SendError(ctx, err)
}
authMode, err := config.AuthMode(ctx)
if err != nil {
return u.SendError(ctx, err)
}
query := model.UserGroup{}
switch authMode {
case common.LDAPAuth:
query.GroupType = common.LDAPGroupType
case common.HTTPAuth:
query.GroupType = common.HTTPGroupType
}
ug, err := u.ctl.List(ctx, query)
if err != nil {
return u.SendError(ctx, err)
}
return operation.NewListUserGroupsOK().WithPayload(getUserGroupResp(ug))
}
func getUserGroupResp(ug []*model.UserGroup) []*models.UserGroup {
result := make([]*models.UserGroup, 0)
for _, u := range ug {
ug := &models.UserGroup{
GroupName: u.GroupName,
GroupType: int64(u.GroupType),
LdapGroupDn: u.LdapGroupDN,
ID: int64(u.ID),
}
result = append(result, ug)
}
return result
}
func (u *userGroupAPI) UpdateUserGroup(ctx context.Context, params operation.UpdateUserGroupParams) middleware.Responder {
if err := u.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourceUserGroup); err != nil {
return u.SendError(ctx, err)
}
if params.GroupID <= 0 {
return operation.NewUpdateUserGroupBadRequest()
}
if params.Usergroup == nil || len(params.Usergroup.GroupName) == 0 {
return operation.NewUpdateUserGroupBadRequest()
}
err := u.ctl.Update(ctx, int(params.GroupID), params.Usergroup.GroupName)
if err != nil {
return u.SendError(ctx, err)
}
return operation.NewUpdateUserGroupOK()
}

View File

@ -31,7 +31,6 @@ func registerLegacyRoutes() {
beego.Router("/api/"+version+"/users/:id/permissions", &api.UserAPI{}, "get:ListUserPermissions")
beego.Router("/api/"+version+"/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole")
beego.Router("/api/"+version+"/users/:id/cli_secret", &api.UserAPI{}, "put:SetCLISecret")
beego.Router("/api/"+version+"/usergroups/?:ugid([0-9]+)", &api.UserGroupAPI{})
beego.Router("/api/"+version+"/email/ping", &api.EmailAPI{}, "post:Ping")
beego.Router("/api/"+version+"/health", &api.HealthAPI{}, "get:CheckHealth")
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")

View File

@ -35,6 +35,7 @@ from pprint import pprint
class TestUserGroup(unittest.TestCase):
"""UserGroup unit test stubs"""
product_api = testutils.GetProductApi("admin", "Harbor12345")
usergroup_api = testutils.GetUserGroupApi("admin", "Harbor12345")
groupId = 0
def setUp(self):
self.conf= Configurations()
@ -44,16 +45,16 @@ class TestUserGroup(unittest.TestCase):
def tearDown(self):
if self.groupId > 0 :
self.product_api.usergroups_group_id_delete(group_id=self.groupId)
self.usergroup_api.delete_user_group(group_id=self.groupId)
pass
def testAddUpdateUserGroup(self):
"""Test UserGroup"""
user_group = UserGroup(group_name="harbor_group123", group_type=1, ldap_group_dn="cn=harbor_group,ou=groups,dc=example,dc=com")
result = self.product_api.usergroups_post(usergroup=user_group)
result = self.usergroup_api.create_user_group(usergroup=user_group)
pprint(result)
user_groups = self.product_api.usergroups_get()
user_groups = self.usergroup_api.list_user_groups()
found = False
for ug in user_groups :
@ -64,9 +65,9 @@ class TestUserGroup(unittest.TestCase):
self.groupId = ug.id
self.assertTrue(found)
result = self.product_api.usergroups_group_id_put(self.groupId, usergroup = UserGroup(group_name = "newharbor_group"))
result = self.usergroup_api.update_user_group(self.groupId, usergroup = UserGroup(group_name = "newharbor_group"))
new_user_group = self.product_api.usergroups_group_id_get(group_id=self.groupId)
new_user_group = self.usergroup_api.get_user_group(group_id=self.groupId)
self.assertEqual("newharbor_group", new_user_group.group_name)
pass

View File

@ -57,8 +57,7 @@ def GetRepositoryApi(username, password, harbor_server= os.environ.get("HARBOR_H
api_instance = v2_swagger_client.RepositoryApi(api_client)
return api_instance
def GetConfigureApi(username, password, harbor_server= os.environ.get("HARBOR_HOST", '')):
def GetUserGroupApi(username, password, harbor_server= os.environ.get("HARBOR_HOST", '')):
cfg = v2_swagger_client.Configuration()
cfg.host = "https://"+harbor_server+"/api/v2.0"
cfg.username = username
@ -66,10 +65,9 @@ def GetConfigureApi(username, password, harbor_server= os.environ.get("HARBOR_HO
cfg.verify_ssl = False
cfg.debug = True
api_client = v2_swagger_client.ApiClient(cfg)
api_instance = v2_swagger_client.Configuration(api_client)
api_instance = v2_swagger_client.UsergroupApi(api_client)
return api_instance
class TestResult(object):
def __init__(self):
self.num_errors = 0