mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-02 13:01:23 +01:00
Merge pull request #14544 from stonezdj/21mar26_usergroup
Refactor user group to new programming model
This commit is contained in:
commit
4e7b530140
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -27,7 +27,6 @@ func init() {
|
||||
new(ProjectMetadata),
|
||||
new(Label),
|
||||
new(ResourceLabel),
|
||||
new(UserGroup),
|
||||
new(JobLog),
|
||||
new(OIDCUser),
|
||||
new(ProjectBlob),
|
||||
|
@ -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"`
|
||||
}
|
||||
|
111
src/controller/usergroup/controller.go
Normal file
111
src/controller/usergroup/controller.go
Normal 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)
|
||||
}
|
108
src/controller/usergroup/test/controller_test.go
Normal file
108
src/controller/usergroup/test/controller_test.go
Normal 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{})
|
||||
}
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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",
|
||||
},
|
||||
|
@ -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
|
||||
}
|
@ -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...)
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
194
src/pkg/usergroup/dao/dao.go
Normal file
194
src/pkg/usergroup/dao/dao.go
Normal 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
|
||||
}
|
67
src/pkg/usergroup/dao/dao_test.go
Normal file
67
src/pkg/usergroup/dao/dao_test.go
Normal 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{})
|
||||
}
|
129
src/pkg/usergroup/manager.go
Normal file
129
src/pkg/usergroup/manager.go
Normal 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
|
||||
}
|
73
src/pkg/usergroup/manager_test.go
Normal file
73
src/pkg/usergroup/manager_test.go
Normal 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{})
|
||||
}
|
@ -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"
|
@ -57,6 +57,7 @@ func New() http.Handler {
|
||||
OidcAPI: newOIDCAPI(),
|
||||
SystemCVEAllowlistAPI: newSystemCVEAllowListAPI(),
|
||||
ConfigureAPI: newConfigAPI(),
|
||||
UsergroupAPI: newUserGroupAPI(),
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
150
src/server/v2.0/handler/usergroup.go
Normal file
150
src/server/v2.0/handler/usergroup.go
Normal 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()
|
||||
}
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user