mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-25 19:56:09 +01:00
Merge pull request #4483 from stonezdj/api4assign_role_to_group4
Add REST API for assign role to group
This commit is contained in:
commit
df63a73fd4
@ -627,6 +627,167 @@ paths:
|
||||
description: Project ID does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/projects/{project_id}/projectmembers':
|
||||
get:
|
||||
summary: Get all project member information
|
||||
description: Get all project member information
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Get project members successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/ProjectMemberEntity'
|
||||
'400':
|
||||
description: The project id is invalid.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User in session does not have permission to the project.
|
||||
'404':
|
||||
description: Project ID does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
post:
|
||||
summary: Create project member
|
||||
description: >-
|
||||
Create project member relationship, the member can be one of the user_member and group_member,
|
||||
The user_member need to specify user_id or username. If the user already exist in harbor DB, specify the user_id,
|
||||
If does not exist in harbor DB, it will SearchAndOnBoard the user.
|
||||
The group_member need to specify id or ldap_group_dn. If the group already exist in harbor DB. specify the user group's id,
|
||||
If does not exist, it will SearchAndOnBoard the group.
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: project_member
|
||||
in: body
|
||||
schema:
|
||||
$ref: '#/definitions/ProjectMember'
|
||||
responses:
|
||||
'201':
|
||||
description: Project member created successfully.
|
||||
'400':
|
||||
description: Illegal format of project member or project id is invalid.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User in session does not have permission to the project.
|
||||
'404':
|
||||
description: Project does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/projects/{project_id}/projectmembers/{mid}':
|
||||
get:
|
||||
summary: Get the project member information
|
||||
description: Get the project member information
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: mid
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: The member ID
|
||||
responses:
|
||||
'200':
|
||||
description: Project member retrieved successfully.
|
||||
schema:
|
||||
$ref: '#/definitions/ProjectMemberEntity'
|
||||
'400':
|
||||
description: Illegal format of project member or invalid project id, member id.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User in session does not have permission to the project.
|
||||
'404':
|
||||
description: Project or projet member does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
put:
|
||||
summary: Update project member
|
||||
description: Update project member relationship
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: mid
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Member ID.
|
||||
- name: role
|
||||
in: body
|
||||
schema:
|
||||
$ref: '#/definitions/RoleRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Project member updated successfully.
|
||||
'400':
|
||||
description: Invalid role id, it should be 1,2 or 3, or invalid project id, or invalid member id.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User in session does not have permission to the project.
|
||||
'404':
|
||||
description: project or project member does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
delete:
|
||||
summary: Delete project member
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: mid
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Member ID.
|
||||
responses:
|
||||
'200':
|
||||
description: Project member deleted successfully.
|
||||
'400':
|
||||
description: The project id or project member id is invalid.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User in session does not have permission to the project.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/statistics:
|
||||
get:
|
||||
summary: Get projects number and repositories number relevant to the user
|
||||
@ -2307,8 +2468,153 @@ paths:
|
||||
$ref: '#/responses/UnsupportedMediaType'
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/ldap/users/search:
|
||||
/ldap/groups/search:
|
||||
get:
|
||||
summary: Search available ldap groups.
|
||||
description: >
|
||||
This endpoint searches the available ldap groups based on related
|
||||
configuration parameters. Support searched by input ladp configuration,
|
||||
load configuration from the system and specific filter.
|
||||
parameters:
|
||||
- name: groupname
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: Ldap group name
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Search ldap group successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/UserGroup'
|
||||
'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.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User in session does not have permission to the user group.
|
||||
'404':
|
||||
description: The LDAP group is not found.
|
||||
'409':
|
||||
description: 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.
|
||||
/ldap/users/search:
|
||||
get:
|
||||
summary: Search available ldap users.
|
||||
description: >
|
||||
This endpoint searches the available ldap users based on related
|
||||
@ -2320,15 +2626,6 @@ paths:
|
||||
type: string
|
||||
required: false
|
||||
description: Registered user ID
|
||||
- name: ldap_conf
|
||||
in: body
|
||||
description: >-
|
||||
ldap search configuration. ldapconf field can input ldap service
|
||||
configuration. If this item are blank, will load default
|
||||
configuration will load current configuration from the system.
|
||||
required: false
|
||||
schema:
|
||||
$ref: '#/definitions/LdapConf'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
@ -2338,14 +2635,10 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/LdapUsers'
|
||||
'400':
|
||||
description: Inviald ldap configuration parameters.
|
||||
'401':
|
||||
description: User need to login first.
|
||||
'403':
|
||||
description: Only admin has this authority.
|
||||
'415':
|
||||
$ref: '#/responses/UnsupportedMediaType'
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/ldap/users/import:
|
||||
@ -2379,7 +2672,7 @@ paths:
|
||||
description: Only admin has this authority.
|
||||
'415':
|
||||
$ref: '#/responses/UnsupportedMediaType'
|
||||
'500':
|
||||
'404':
|
||||
description: Failed import some users.
|
||||
schema:
|
||||
type: array
|
||||
@ -3440,3 +3733,70 @@ definitions:
|
||||
update_time:
|
||||
type: string
|
||||
description: The update time of label.
|
||||
ProjectMemberEntity:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: the project member id
|
||||
project_id:
|
||||
type: integer
|
||||
description: the project id
|
||||
entity_name:
|
||||
type: string
|
||||
description: the name of the group member.
|
||||
role_name:
|
||||
type: string
|
||||
description: the name of the role
|
||||
role_id:
|
||||
type: integer
|
||||
description: the role id
|
||||
entity_id:
|
||||
type: integer
|
||||
description: the id of entity, if the member is an user, it is user_id in user table. if the member is an user group, it is the user group's ID in user_group table.
|
||||
entity_type:
|
||||
type: string
|
||||
description: the entity's type, u for user entity, g for group entity.
|
||||
ProjectMember:
|
||||
type: object
|
||||
properties:
|
||||
role_id:
|
||||
type: integer
|
||||
description: The role id 1 for projectAdmin, 2 for developer, 3 for guest
|
||||
member_user:
|
||||
$ref: '#/definitions/UserEntity'
|
||||
member_group:
|
||||
$ref: '#/definitions/UserGroup'
|
||||
RoleRequest:
|
||||
type: object
|
||||
properties:
|
||||
role_id:
|
||||
type: integer
|
||||
description: The role id 1 for projectAdmin, 2 for developer, 3 for guest
|
||||
UserEntity:
|
||||
type: object
|
||||
properties:
|
||||
user_id:
|
||||
type: integer
|
||||
description: The ID of the user.
|
||||
username:
|
||||
type: string
|
||||
description: The name of the user.
|
||||
UserGroup:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: The ID of the user group
|
||||
group_name:
|
||||
type: string
|
||||
description: The name of the user group
|
||||
group_type:
|
||||
type: integer
|
||||
description: The group type, 1 for LDAP group.
|
||||
ldap_group_dn:
|
||||
type: string
|
||||
description: The DN of the LDAP group if group type is 1 (LDAP group).
|
||||
|
||||
|
||||
|
||||
|
@ -106,4 +106,5 @@ const (
|
||||
DefaultJobserviceEndpoint = "http://jobservice:8080"
|
||||
DefaultUIEndpoint = "http://ui:8080"
|
||||
DefaultNotaryEndpoint = "http://notary-server:4443"
|
||||
LdapGroupType = 1
|
||||
)
|
||||
|
@ -50,6 +50,10 @@ func QueryUserGroup(query models.UserGroup) ([]*models.UserGroup, error) {
|
||||
sql += ` and ldap_group_dn = ? `
|
||||
sqlParam = append(sqlParam, 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
|
||||
@ -60,12 +64,14 @@ func QueryUserGroup(query models.UserGroup) ([]*models.UserGroup, error) {
|
||||
// GetUserGroup ...
|
||||
func GetUserGroup(id int) (*models.UserGroup, error) {
|
||||
userGroup := models.UserGroup{ID: id}
|
||||
o := dao.GetOrmer()
|
||||
err := o.Read(&userGroup)
|
||||
userGroupList, err := QueryUserGroup(userGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userGroup, nil
|
||||
if len(userGroupList) > 0 {
|
||||
return userGroupList[0], nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DeleteUserGroup ...
|
||||
@ -92,3 +98,30 @@ func UpdateUserGroupName(id int, groupName string) error {
|
||||
_, err := o.Raw(sql, groupName, id).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
// 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.
|
||||
// This is used for ldap and uaa authentication, such the usergroup can have an ID in Harbor.
|
||||
// the keyAttribute and combinedKeyAttribute are key columns used to check duplicate usergroup in harbor
|
||||
func OnBoardUserGroup(g *models.UserGroup, keyAttribute string, combinedKeyAttributes ...string) error {
|
||||
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
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
@ -80,7 +81,7 @@ func TestAddUserGroup(t *testing.T) {
|
||||
want int
|
||||
wantErr bool
|
||||
}{
|
||||
{"Insert an ldap user group", args{userGroup: models.UserGroup{GroupName: "sample_group", GroupType: 1, LdapGroupDN: "sample_ldap_dn_string"}}, 0, false},
|
||||
{"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 {
|
||||
@ -108,8 +109,8 @@ func TestQueryUserGroup(t *testing.T) {
|
||||
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: 1}}, 2, false},
|
||||
{"Query ldap group with group property", args{query: models.UserGroup{GroupType: 1, LdapGroupDN: "CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com"}}, 1, false},
|
||||
{"Query all ldap group", args{query: models.UserGroup{GroupType: common.LdapGroupType}}, 2, 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) {
|
||||
@ -126,7 +127,7 @@ func TestQueryUserGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUserGroup(t *testing.T) {
|
||||
userGroup := models.UserGroup{GroupName: "insert_group", GroupType: 1, LdapGroupDN: "ldap_dn_string"}
|
||||
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)
|
||||
@ -142,6 +143,7 @@ func TestGetUserGroup(t *testing.T) {
|
||||
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) {
|
||||
@ -150,7 +152,7 @@ func TestGetUserGroup(t *testing.T) {
|
||||
t.Errorf("GetUserGroup() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got.GroupName != tt.want {
|
||||
if got != nil && got.GroupName != tt.want {
|
||||
t.Errorf("GetUserGroup() = %v, want %v", got.GroupName, tt.want)
|
||||
}
|
||||
})
|
||||
@ -216,3 +218,34 @@ func TestDeleteUserGroup(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := OnBoardUserGroup(tt.args.g, "LdapGroupDN", "GroupType"); (err != nil) != tt.wantErr {
|
||||
t.Errorf("OnBoardUserGroup() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ func GetProjectMember(queryMember models.Member) ([]*models.Member, error) {
|
||||
func AddProjectMember(member models.Member) (int, error) {
|
||||
|
||||
log.Debugf("Adding project member %+v", member)
|
||||
|
||||
o := dao.GetOrmer()
|
||||
|
||||
if member.EntityID <= 0 {
|
||||
@ -98,9 +99,7 @@ func AddProjectMember(member models.Member) (int, error) {
|
||||
|
||||
// UpdateProjectMemberRole updates the record in table project_member, only role can be changed
|
||||
func UpdateProjectMemberRole(pmID int, role int) error {
|
||||
if role <= 0 || role >= 3 {
|
||||
return fmt.Errorf("Failed to update project member, role is not in 0,1,2, role:%v", role)
|
||||
}
|
||||
|
||||
o := dao.GetOrmer()
|
||||
sql := "update project_member set role = ? where id = ? "
|
||||
_, err := o.Raw(sql, role, pmID).Exec()
|
||||
|
@ -58,5 +58,5 @@ type LdapFailedImportUser struct {
|
||||
// LdapGroup ...
|
||||
type LdapGroup struct {
|
||||
GroupName string `json:"group_name,omitempty"`
|
||||
GroupDN string `json:"group_dn,omitempty"`
|
||||
GroupDN string `json:"ldap_group_dn,omitempty"`
|
||||
}
|
||||
|
@ -108,9 +108,11 @@ func init() {
|
||||
beego.Router("/api/projects/:id([0-9]+)/metadatas/?:name", &MetadataAPI{}, "get:Get")
|
||||
beego.Router("/api/projects/:id([0-9]+)/metadatas/", &MetadataAPI{}, "post:Post")
|
||||
beego.Router("/api/projects/:id([0-9]+)/metadatas/:name", &MetadataAPI{}, "put:Put;delete:Delete")
|
||||
beego.Router("/api/projects/:pid([0-9]+)/projectmembers/?:pmid([0-9]+)", &ProjectMemberAPI{})
|
||||
beego.Router("/api/repositories", &RepositoryAPI{})
|
||||
beego.Router("/api/statistics", &StatisticAPI{})
|
||||
beego.Router("/api/users/?:id", &UserAPI{})
|
||||
beego.Router("/api/usergroups/?:ugid([0-9]+)", &UserGroupAPI{})
|
||||
beego.Router("/api/logs", &LogAPI{})
|
||||
beego.Router("/api/repositories/*", &RepositoryAPI{}, "put:Put")
|
||||
beego.Router("/api/repositories/*/labels", &RepositoryLabelAPI{}, "get:GetOfRepository;post:AddToRepository")
|
||||
|
@ -15,7 +15,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
ldapUtils "github.com/vmware/harbor/src/common/utils/ldap"
|
||||
@ -62,8 +62,7 @@ func (l *LdapAPI) Ping() {
|
||||
if string(l.Ctx.Input.RequestBody) == "" {
|
||||
ldapSession, err = ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
log.Errorf("Can't load system configuration, error: %v", err)
|
||||
l.RenderError(http.StatusInternalServerError, pingErrorMessage)
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't load system configuration, error: %v", err))
|
||||
return
|
||||
}
|
||||
err = ldapSession.ConnectionTest()
|
||||
@ -73,9 +72,7 @@ func (l *LdapAPI) Ping() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("ldap connect fail, error: %v", err)
|
||||
// do not return any detail information of the error, or may cause SSRF security issue #3755
|
||||
l.RenderError(http.StatusBadRequest, pingErrorMessage)
|
||||
l.HandleInternalServerError(fmt.Sprintf("LDAP connect fail, error: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -84,24 +81,9 @@ func (l *LdapAPI) Ping() {
|
||||
func (l *LdapAPI) Search() {
|
||||
var err error
|
||||
var ldapUsers []models.LdapUser
|
||||
var ldapConfs models.LdapConf
|
||||
var ldapSession *ldapUtils.Session
|
||||
l.Ctx.Input.CopyBody(1 << 32)
|
||||
if string(l.Ctx.Input.RequestBody) == "" {
|
||||
ldapSession, err = ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
log.Errorf("can't load system configuration, error: %v", err)
|
||||
l.RenderError(http.StatusInternalServerError, loadSystemErrorMessage)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
l.DecodeJSONReqAndValidate(&ldapConfs)
|
||||
ldapSession, err = ldapUtils.CreateWithConfig(ldapConfs)
|
||||
}
|
||||
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
log.Errorf("can't Open ldap session, error: %v", err)
|
||||
l.RenderError(http.StatusInternalServerError, canNotOpenLdapSession)
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't Open LDAP session, error: %v", err))
|
||||
return
|
||||
}
|
||||
defer ldapSession.Close()
|
||||
@ -111,8 +93,7 @@ func (l *LdapAPI) Search() {
|
||||
ldapUsers, err = ldapSession.SearchUser(searchName)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Ldap search fail, error: %v", err)
|
||||
l.RenderError(http.StatusBadRequest, searchLdapFailMessage)
|
||||
l.HandleInternalServerError(fmt.Sprintf("LDAP search fail, error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@ -132,14 +113,12 @@ func (l *LdapAPI) ImportUser() {
|
||||
ldapFailedImportUsers, err := importUsers(ldapConfs, ldapImportUsers.LdapUIDList)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Ldap import user fail, error: %v", err)
|
||||
l.RenderError(http.StatusBadRequest, importUserError)
|
||||
l.HandleInternalServerError(fmt.Sprintf("LDAP import user fail, error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if len(ldapFailedImportUsers) > 0 {
|
||||
log.Errorf("Import ldap user have internal error")
|
||||
l.RenderError(http.StatusInternalServerError, importUserError)
|
||||
l.HandleNotFound("Import LDAP user have internal error")
|
||||
l.Data["json"] = ldapFailedImportUsers
|
||||
l.ServeJSON()
|
||||
return
|
||||
@ -153,12 +132,12 @@ func importUsers(ldapConfs models.LdapConf, ldapImportUsers []string) ([]models.
|
||||
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
log.Errorf("can't load system configuration, error: %v", err)
|
||||
log.Errorf("Can't load system configuration, error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
log.Errorf("Can't connect to ldap, error: %v", err)
|
||||
log.Errorf("Can't connect to LDAP, error: %v", err)
|
||||
}
|
||||
defer ldapSession.Close()
|
||||
|
||||
@ -182,7 +161,7 @@ func importUsers(ldapConfs models.LdapConf, ldapImportUsers []string) ([]models.
|
||||
u.UID = tempUID
|
||||
u.Error = "failed_search_user"
|
||||
failedImportUser = append(failedImportUser, u)
|
||||
log.Errorf("Invalid ldap search request for %s, error: %v", tempUID, err)
|
||||
log.Errorf("Invalid LDAP search request for %s, error: %v", tempUID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -211,3 +190,22 @@ func importUsers(ldapConfs models.LdapConf, ldapImportUsers []string) ([]models.
|
||||
|
||||
return failedImportUser, nil
|
||||
}
|
||||
|
||||
// SearchGroup ... Search LDAP by groupname
|
||||
func (l *LdapAPI) SearchGroup() {
|
||||
searchName := l.GetString("groupname")
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't get LDAP system config, error: %v", err))
|
||||
return
|
||||
}
|
||||
ldapSession.Open()
|
||||
defer ldapSession.Close()
|
||||
ldapGroups, err := ldapSession.SearchGroupByName(searchName)
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't search LDAP group by name, error: %v", err))
|
||||
return
|
||||
}
|
||||
l.Data["json"] = ldapGroups
|
||||
l.ServeJSON()
|
||||
}
|
||||
|
206
src/ui/api/projectmember.go
Normal file
206
src/ui/api/projectmember.go
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao/project"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
)
|
||||
|
||||
// ProjectMemberAPI handles request to /api/projects/{}/members/{}
|
||||
type ProjectMemberAPI struct {
|
||||
BaseController
|
||||
id int
|
||||
entityID int
|
||||
entityType string
|
||||
project *models.Project
|
||||
}
|
||||
|
||||
// Prepare validates the URL and parms
|
||||
func (pma *ProjectMemberAPI) Prepare() {
|
||||
pma.BaseController.Prepare()
|
||||
|
||||
if !pma.SecurityCtx.IsAuthenticated() {
|
||||
pma.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
pid, err := pma.GetInt64FromPath(":pid")
|
||||
if err != nil || pid <= 0 {
|
||||
text := "invalid project ID: "
|
||||
if err != nil {
|
||||
text += err.Error()
|
||||
} else {
|
||||
text += fmt.Sprintf("%d", pid)
|
||||
}
|
||||
pma.HandleBadRequest(text)
|
||||
return
|
||||
}
|
||||
project, err := pma.ProjectMgr.Get(pid)
|
||||
if err != nil {
|
||||
pma.ParseAndHandleError(fmt.Sprintf("failed to get project %d", pid), err)
|
||||
return
|
||||
}
|
||||
if project == nil {
|
||||
pma.HandleNotFound(fmt.Sprintf("project %d not found", pid))
|
||||
return
|
||||
}
|
||||
pma.project = project
|
||||
|
||||
if !(pma.Ctx.Input.IsGet() && pma.SecurityCtx.HasReadPerm(pid) ||
|
||||
pma.SecurityCtx.HasAllPerm(pid)) {
|
||||
pma.HandleForbidden(pma.SecurityCtx.GetUsername())
|
||||
return
|
||||
}
|
||||
|
||||
pmid, err := pma.GetInt64FromPath(":pmid")
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get pmid from path, error %v", err)
|
||||
}
|
||||
if pmid <= 0 && (pma.Ctx.Input.IsPut() || pma.Ctx.Input.IsDelete()) {
|
||||
pma.HandleBadRequest(fmt.Sprintf("The project member id is invalid, pmid:%s", pma.GetStringFromPath(":pmid")))
|
||||
return
|
||||
}
|
||||
pma.id = int(pmid)
|
||||
}
|
||||
|
||||
//Get ...
|
||||
func (pma *ProjectMemberAPI) Get() {
|
||||
projectID := pma.project.ProjectID
|
||||
queryMember := models.Member{}
|
||||
queryMember.ProjectID = projectID
|
||||
pma.Data["json"] = make([]models.Member, 0)
|
||||
if pma.id == 0 {
|
||||
//member id not set, return all member of current project
|
||||
memberList, err := project.GetProjectMember(queryMember)
|
||||
if err != nil {
|
||||
pma.HandleInternalServerError(fmt.Sprintf("Failed to query database for member list, error: %v", err))
|
||||
return
|
||||
}
|
||||
if len(memberList) > 0 {
|
||||
pma.Data["json"] = memberList
|
||||
}
|
||||
} else {
|
||||
//return a specific member
|
||||
queryMember.ID = pma.id
|
||||
memberList, err := project.GetProjectMember(queryMember)
|
||||
if err != nil {
|
||||
pma.HandleInternalServerError(fmt.Sprintf("Failed to query database for member list, error: %v", err))
|
||||
return
|
||||
}
|
||||
if len(memberList) == 0 {
|
||||
pma.HandleNotFound(fmt.Sprintf("The project member does not exit, pmid:%v", pma.id))
|
||||
return
|
||||
}
|
||||
pma.Data["json"] = memberList[0]
|
||||
}
|
||||
pma.ServeJSON()
|
||||
}
|
||||
|
||||
// Post ... Add a project member
|
||||
func (pma *ProjectMemberAPI) Post() {
|
||||
projectID := pma.project.ProjectID
|
||||
var request models.MemberReq
|
||||
pma.DecodeJSONReq(&request)
|
||||
pmid, err := AddOrUpdateProjectMember(projectID, request)
|
||||
if err != nil {
|
||||
pma.HandleInternalServerError(fmt.Sprintf("Failed to add project member, error: %v", err))
|
||||
return
|
||||
}
|
||||
pma.Redirect(http.StatusCreated, strconv.FormatInt(int64(pmid), 10))
|
||||
}
|
||||
|
||||
// Put ... Update an exist project member
|
||||
func (pma *ProjectMemberAPI) Put() {
|
||||
pid := pma.project.ProjectID
|
||||
pmID := pma.id
|
||||
var req models.Member
|
||||
pma.DecodeJSONReq(&req)
|
||||
if req.Role < 1 || req.Role > 3 {
|
||||
pma.HandleBadRequest(fmt.Sprintf("Invalid role id %v", req.Role))
|
||||
return
|
||||
}
|
||||
err := project.UpdateProjectMemberRole(pmID, req.Role)
|
||||
if err != nil {
|
||||
pma.HandleInternalServerError(fmt.Sprintf("Failed to update DB to add project user role, project id: %d, pmid : %d, role id: %d", pid, pmID, req.Role))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (pma *ProjectMemberAPI) Delete() {
|
||||
pmid := pma.id
|
||||
err := project.DeleteProjectMemberByID(pmid)
|
||||
if err != nil {
|
||||
pma.HandleInternalServerError(fmt.Sprintf("Failed to delete project roles for user, project member id: %d, error: %v", pmid, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// AddOrUpdateProjectMember ... If the project member relationship does not exist, create it. if exist, update it
|
||||
func AddOrUpdateProjectMember(projectID int64, request models.MemberReq) (int, error) {
|
||||
var member models.Member
|
||||
member.ProjectID = projectID
|
||||
member.Role = request.Role
|
||||
if request.MemberUser.UserID > 0 {
|
||||
member.EntityID = request.MemberUser.UserID
|
||||
member.EntityType = common.UserMember
|
||||
} else if request.MemberGroup.ID > 0 {
|
||||
member.EntityID = request.MemberGroup.ID
|
||||
member.EntityType = common.GroupMember
|
||||
} else if len(request.MemberUser.Username) > 0 {
|
||||
member.EntityType = common.UserMember
|
||||
userID, err := auth.SearchAndOnBoardUser(request.MemberUser.Username)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
member.EntityID = userID
|
||||
} else if len(request.MemberGroup.LdapGroupDN) > 0 {
|
||||
member.EntityType = common.GroupMember
|
||||
//If groupname provided, use the provided groupname
|
||||
//If ldap group already exist in harbor, use the previous group name
|
||||
groupID, err := auth.SearchAndOnBoardGroup(request.MemberGroup.LdapGroupDN, request.MemberGroup.GroupName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
member.EntityID = groupID
|
||||
}
|
||||
if member.EntityID <= 0 {
|
||||
return 0, fmt.Errorf("Can not get valid member entity, request: %+v", request)
|
||||
}
|
||||
memberList, err := project.GetProjectMember(models.Member{
|
||||
ProjectID: member.ProjectID,
|
||||
EntityID: member.EntityID,
|
||||
EntityType: member.EntityType,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(memberList) > 0 {
|
||||
project.UpdateProjectMemberRole(memberList[0].ID, member.Role)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if member.Role < 1 || member.Role > 3 {
|
||||
return 0, fmt.Errorf("Failed to update project member, role is not in 1,2,3 role:%v", member.Role)
|
||||
}
|
||||
return project.AddProjectMember(member)
|
||||
}
|
210
src/ui/api/projectmember_test.go
Normal file
210
src/ui/api/projectmember_test.go
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/dao/project"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
func TestProjectMemberAPI_Get(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/projects/1/projectmembers",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
//200
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/projects/1/projectmembers",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
//400
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/projects/0/projectmembers",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
// 404
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/projects/1/projectmembers/121",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestProjectMemberAPI_Post(t *testing.T) {
|
||||
userID, err := dao.Register(models.User{
|
||||
Username: "restuser",
|
||||
Password: "Harbor12345",
|
||||
Email: "restuser@example.com",
|
||||
})
|
||||
defer dao.DeleteUser(int(userID))
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred when create user: %v", err)
|
||||
}
|
||||
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/projectmembers",
|
||||
bodyJSON: &models.MemberReq{
|
||||
Role: 1,
|
||||
MemberUser: models.User{
|
||||
UserID: int(userID),
|
||||
},
|
||||
},
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/projectmembers",
|
||||
bodyJSON: &models.MemberReq{
|
||||
Role: 1,
|
||||
MemberUser: models.User{
|
||||
UserID: int(userID),
|
||||
},
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusCreated,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/projectmembers",
|
||||
bodyJSON: &models.MemberReq{
|
||||
Role: 1,
|
||||
MemberUser: models.User{
|
||||
UserID: 0,
|
||||
},
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestProjectMemberAPI_PutAndDelete(t *testing.T) {
|
||||
|
||||
userID, err := dao.Register(models.User{
|
||||
Username: "restuser",
|
||||
Password: "Harbor12345",
|
||||
Email: "restuser@example.com",
|
||||
})
|
||||
defer dao.DeleteUser(int(userID))
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred when create user: %v", err)
|
||||
}
|
||||
|
||||
ID, err := project.AddProjectMember(models.Member{
|
||||
ProjectID: 1,
|
||||
Role: 1,
|
||||
EntityID: int(userID),
|
||||
EntityType: "u",
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred when add project member: %v", err)
|
||||
}
|
||||
URL := fmt.Sprintf("/api/projects/1/projectmembers/%v", ID)
|
||||
badURL := fmt.Sprintf("/api/projects/1/projectmembers/%v", 0)
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: URL,
|
||||
bodyJSON: &models.Member{
|
||||
Role: 2,
|
||||
},
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
// 200
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: URL,
|
||||
bodyJSON: &models.Member{
|
||||
Role: 2,
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
// 400
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: badURL,
|
||||
bodyJSON: &models.Member{
|
||||
Role: 2,
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
// 400
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: URL,
|
||||
bodyJSON: &models.Member{
|
||||
Role: -2,
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
// 200
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: URL,
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
runCodeCheckingCases(t, cases...)
|
||||
|
||||
}
|
146
src/ui/api/usergroup.go
Normal file
146
src/ui/api/usergroup.go
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao/group"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
)
|
||||
|
||||
// UserGroupAPI ...
|
||||
type UserGroupAPI struct {
|
||||
BaseController
|
||||
id int
|
||||
}
|
||||
|
||||
// Prepare validates the URL and parms
|
||||
func (uga *UserGroupAPI) Prepare() {
|
||||
uga.BaseController.Prepare()
|
||||
if !uga.SecurityCtx.IsAuthenticated() {
|
||||
uga.HandleUnauthorized()
|
||||
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.HandleBadRequest(fmt.Sprintf("invalid user group ID: %s", uga.GetStringFromPath(":ugid")))
|
||||
return
|
||||
}
|
||||
uga.id = int(ugid)
|
||||
//Common user can create/update, only harbor admin can delete user group.
|
||||
if uga.Ctx.Input.IsDelete() && !uga.SecurityCtx.IsSysAdmin() {
|
||||
uga.HandleForbidden(uga.SecurityCtx.GetUsername())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 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: common.LdapGroupType} //Current query LDAP group only
|
||||
userGroupList, err := group.QueryUserGroup(query)
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("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.HandleNotFound("The user group does not exist.")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("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{}
|
||||
uga.DecodeJSONReq(&userGroup)
|
||||
userGroup.ID = 0
|
||||
userGroup.GroupType = common.LdapGroupType
|
||||
query := models.UserGroup{GroupType: userGroup.GroupType, LdapGroupDN: userGroup.LdapGroupDN}
|
||||
result, err := group.QueryUserGroup(query)
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in add user group, error: %v", err))
|
||||
return
|
||||
}
|
||||
if len(result) > 0 {
|
||||
uga.HandleConflict("Error occurred in add user group, duplicate user group exist.")
|
||||
}
|
||||
// User can not add ldap group when the ldap server is offline
|
||||
ldapGroup, err := auth.SearchGroup(userGroup.LdapGroupDN)
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in search user group. error: %v", err))
|
||||
return
|
||||
}
|
||||
if ldapGroup == nil {
|
||||
uga.HandleNotFound("The LDAP group is not found")
|
||||
return
|
||||
}
|
||||
groupID, err := group.AddUserGroup(userGroup)
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("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{}
|
||||
uga.DecodeJSONReq(&userGroup)
|
||||
ID := uga.id
|
||||
userGroup.GroupType = common.LdapGroupType
|
||||
log.Debugf("Updated user group %v", userGroup)
|
||||
err := group.UpdateUserGroupName(ID, userGroup.GroupName)
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in update user group, error: %v", err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (uga *UserGroupAPI) Delete() {
|
||||
err := group.DeleteUserGroup(uga.id)
|
||||
if err != nil {
|
||||
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in update user group, error: %v", err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
154
src/ui/api/usergroup_test.go
Normal file
154
src/ui/api/usergroup_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
|
||||
"github.com/vmware/harbor/src/common/dao/group"
|
||||
|
||||
"github.com/vmware/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
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: URL,
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
|
||||
// 200
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("/api/usergroups/%d", groupID),
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
// 200
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("/api/usergroups"),
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
// 200
|
||||
&codeCheckingCase{
|
||||
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
|
||||
&codeCheckingCase{
|
||||
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,
|
||||
},
|
||||
}
|
||||
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
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("/api/usergroups/%d", groupID),
|
||||
bodyJSON: &models.UserGroup{
|
||||
GroupName: "my_group",
|
||||
},
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
//200
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("/api/usergroups/%d", groupID),
|
||||
bodyJSON: &models.UserGroup{
|
||||
GroupName: "my_group",
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
@ -68,7 +68,7 @@ func TestDefaultAuthenticate(t *testing.T) {
|
||||
authHelper := DefaultAuthenticateHelper{}
|
||||
m := models.AuthModel{}
|
||||
user, err := authHelper.Authenticate(m)
|
||||
if user != nil || err != nil {
|
||||
if user != nil || err == nil {
|
||||
t.Fatal("Default implementation should return nil")
|
||||
}
|
||||
}
|
||||
@ -77,8 +77,26 @@ func TestDefaultOnBoardUser(t *testing.T) {
|
||||
user := &models.User{}
|
||||
authHelper := DefaultAuthenticateHelper{}
|
||||
err := authHelper.OnBoardUser(user)
|
||||
if err != nil {
|
||||
t.Fatal("Default implementation should return nil")
|
||||
if err == nil {
|
||||
t.Fatal("Default implementation should return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultMethods(t *testing.T) {
|
||||
authHelper := DefaultAuthenticateHelper{}
|
||||
_, err := authHelper.SearchUser("sample")
|
||||
if err == nil {
|
||||
t.Fatal("Default implementation should return error")
|
||||
}
|
||||
|
||||
_, err = authHelper.SearchGroup("sample")
|
||||
if err == nil {
|
||||
t.Fatal("Default implementation should return error")
|
||||
}
|
||||
|
||||
err = authHelper.OnBoardGroup(&models.UserGroup{}, "sample")
|
||||
if err == nil {
|
||||
t.Fatal("Default implementation should return error")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -55,9 +56,13 @@ type AuthenticateHelper interface {
|
||||
// put the id in the pointer of user model, if it does exist, fill in the user model based
|
||||
// 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
|
||||
// Get user information from account repository
|
||||
SearchUser(username string) (*models.User, error)
|
||||
// Update user information after authenticate, such as Onboard or sync info etc
|
||||
// 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
|
||||
PostAuthenticate(u *models.User) error
|
||||
}
|
||||
|
||||
@ -67,26 +72,36 @@ type DefaultAuthenticateHelper struct {
|
||||
|
||||
// Authenticate ...
|
||||
func (d *DefaultAuthenticateHelper) Authenticate(m models.AuthModel) (*models.User, error) {
|
||||
return nil, nil
|
||||
return nil, errors.New("Not supported")
|
||||
}
|
||||
|
||||
// OnBoardUser will check if a user exists in user table, if not insert the user and
|
||||
// put the id in the pointer of user model, if it does exist, fill in the user model based
|
||||
// on the data record of the user
|
||||
func (d *DefaultAuthenticateHelper) OnBoardUser(u *models.User) error {
|
||||
return nil
|
||||
return errors.New("Not supported")
|
||||
}
|
||||
|
||||
//SearchUser - Get user information from account repository
|
||||
func (d *DefaultAuthenticateHelper) SearchUser(username string) (*models.User, error) {
|
||||
return nil, nil
|
||||
return nil, errors.New("Not supported")
|
||||
}
|
||||
|
||||
//PostAuthenticate - Update user information after authenticate, such as Onboard or sync info etc
|
||||
//PostAuthenticate - Update user information after authenticate, such as OnBoard or sync info etc
|
||||
func (d *DefaultAuthenticateHelper) PostAuthenticate(u *models.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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) {
|
||||
return nil, errors.New("Not supported")
|
||||
}
|
||||
|
||||
var registry = make(map[string]AuthenticateHelper)
|
||||
|
||||
// Register add different authenticators to registry map.
|
||||
@ -128,9 +143,7 @@ func Login(m models.AuthModel) (*models.User, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = authenticator.PostAuthenticate(user)
|
||||
|
||||
return user, err
|
||||
}
|
||||
|
||||
@ -166,6 +179,51 @@ func SearchUser(username string) (*models.User, error) {
|
||||
return helper.SearchUser(username)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
helper, err := getHelper()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return helper.OnBoardGroup(userGroup, altGroupName)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
helper, err := getHelper()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return helper.SearchGroup(groupKey)
|
||||
}
|
||||
|
||||
// SearchAndOnBoardUser ... Search user and OnBoard user, if user exist, return the ID of current user.
|
||||
func SearchAndOnBoardUser(username string) (int, error) {
|
||||
user, err := SearchUser(username)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if user != nil {
|
||||
err = OnBoardUser(user)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return user.UserID, nil
|
||||
}
|
||||
|
||||
// SearchAndOnBoardGroup ... if altGroupName is not empty, take the altGroupName as groupName in harbor DB
|
||||
func SearchAndOnBoardGroup(groupKey, altGroupName string) (int, error) {
|
||||
userGroup, err := SearchGroup(groupKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if userGroup != nil {
|
||||
err = OnBoardGroup(userGroup, altGroupName)
|
||||
}
|
||||
return userGroup.ID, err
|
||||
}
|
||||
|
||||
// PostAuthenticate -
|
||||
func PostAuthenticate(u *models.User) error {
|
||||
helper, err := getHelper()
|
||||
|
@ -126,7 +126,7 @@ func TestSearchUser(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestAuthenticateHelperOnboardUser(t *testing.T) {
|
||||
func TestAuthenticateHelperOnBoardUser(t *testing.T) {
|
||||
user := models.User{
|
||||
Username: "test01",
|
||||
Realname: "test01",
|
||||
|
@ -19,7 +19,10 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/dao/group"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
ldapUtils "github.com/vmware/harbor/src/common/utils/ldap"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
@ -130,6 +133,45 @@ func (l *Auth) SearchUser(username string) (*models.User, error) {
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
//SearchGroup -- Search group in ldap authenticator, groupKey is LDAP group DN.
|
||||
func (l *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not load system ldap config: %v", err)
|
||||
}
|
||||
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
log.Warningf("ldap connection fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer ldapSession.Close()
|
||||
userGroupList, err := ldapSession.SearchGroupByDN(groupKey)
|
||||
|
||||
if err != nil {
|
||||
log.Warningf("ldap search group fail: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(userGroupList) == 0 {
|
||||
return nil, fmt.Errorf("Failed to searh ldap group with groupDN:%v", groupKey)
|
||||
}
|
||||
userGroup := models.UserGroup{
|
||||
GroupName: userGroupList[0].GroupName,
|
||||
LdapGroupDN: userGroupList[0].GroupDN,
|
||||
}
|
||||
return &userGroup, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if len(altGroupName) > 0 {
|
||||
u.GroupName = altGroupName
|
||||
}
|
||||
u.GroupType = common.LdapGroupType
|
||||
return group.OnBoardUserGroup(u, "LdapGroupDN", "GroupType")
|
||||
}
|
||||
|
||||
//PostAuthenticate -- If user exist in harbor DB, sync email address, if not exist, call OnBoardUser
|
||||
func (l *Auth) PostAuthenticate(u *models.User) error {
|
||||
|
||||
|
@ -22,9 +22,11 @@ import (
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/dao/project"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/common/utils/test"
|
||||
"github.com/vmware/harbor/src/ui/api"
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
uiConfig "github.com/vmware/harbor/src/ui/config"
|
||||
)
|
||||
@ -46,7 +48,7 @@ var adminServerLdapTestConfig = map[string]interface{}{
|
||||
common.LDAPBaseDN: "dc=example,dc=com",
|
||||
common.LDAPUID: "uid",
|
||||
common.LDAPFilter: "",
|
||||
common.LDAPScope: 3,
|
||||
common.LDAPScope: 2,
|
||||
common.LDAPTimeout: 30,
|
||||
// config.TokenServiceURL: "",
|
||||
// config.RegistryURL: "",
|
||||
@ -64,6 +66,10 @@ var adminServerLdapTestConfig = map[string]interface{}{
|
||||
common.CfgExpiration: 5,
|
||||
// config.JobLogDir: "/var/log/jobs",
|
||||
common.AdminInitialPassword: "password",
|
||||
common.LDAPGroupSearchFilter: "objectclass=groupOfNames",
|
||||
common.LDAPGroupBaseDN: "dc=example,dc=com",
|
||||
common.LDAPGroupAttributeName: "cn",
|
||||
common.LDAPGroupSearchScope: 2,
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@ -102,6 +108,24 @@ func TestMain(m *testing.M) {
|
||||
log.Fatalf("failed to initialize database: %v", err)
|
||||
}
|
||||
|
||||
//Extract to test utils
|
||||
initSqls := []string{
|
||||
"insert into user (username, email, password, realname) values ('member_test_01', 'member_test_01@example.com', '123456', 'member_test_01')",
|
||||
"insert into project (name, owner_id) values ('member_test_01', 1)",
|
||||
"insert into user_group (group_name, group_type, group_property) values ('test_group_01', 1, 'CN=harbor_users,OU=sample,OU=vmware,DC=harbor,DC=com')",
|
||||
"update project set owner_id = (select user_id from 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 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)",
|
||||
}
|
||||
|
||||
clearSqls := []string{
|
||||
"delete from project where name='member_test_01'",
|
||||
"delete from user where username='member_test_01' or username='pm_sample'",
|
||||
"delete from user_group",
|
||||
"delete from project_member",
|
||||
}
|
||||
dao.PrepareTestData(clearSqls, initSqls)
|
||||
|
||||
retCode := m.Run()
|
||||
os.Exit(retCode)
|
||||
}
|
||||
@ -168,7 +192,7 @@ func TestSearchUser_02(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestOnboardUser(t *testing.T) {
|
||||
func TestOnBoardUser(t *testing.T) {
|
||||
user := &models.User{
|
||||
Username: "sample",
|
||||
Email: "sample@example.com",
|
||||
@ -186,7 +210,7 @@ func TestOnboardUser(t *testing.T) {
|
||||
assert.Equal(t, "sample@example.com", user.Email)
|
||||
}
|
||||
|
||||
func TestOnboardUser_02(t *testing.T) {
|
||||
func TestOnBoardUser_02(t *testing.T) {
|
||||
user := &models.User{
|
||||
Username: "sample02",
|
||||
Realname: "Sample02",
|
||||
@ -204,7 +228,7 @@ func TestOnboardUser_02(t *testing.T) {
|
||||
dao.CleanUser(int64(user.UserID))
|
||||
}
|
||||
|
||||
func TestOnboardUser_03(t *testing.T) {
|
||||
func TestOnBoardUser_03(t *testing.T) {
|
||||
user := &models.User{
|
||||
Username: "sample03@example.com",
|
||||
Realname: "Sample03",
|
||||
@ -222,7 +246,7 @@ func TestOnboardUser_03(t *testing.T) {
|
||||
dao.CleanUser(int64(user.UserID))
|
||||
}
|
||||
|
||||
func TestAuthenticateHelperOnboardUser(t *testing.T) {
|
||||
func TestAuthenticateHelperOnBoardUser(t *testing.T) {
|
||||
user := models.User{
|
||||
Username: "test01",
|
||||
Realname: "test01",
|
||||
@ -240,6 +264,21 @@ func TestAuthenticateHelperOnboardUser(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestOnBoardGroup(t *testing.T) {
|
||||
group := models.UserGroup{
|
||||
GroupName: "harbor_group2",
|
||||
LdapGroupDN: "cn=harbor_group2,ou=groups,dc=example,dc=com",
|
||||
}
|
||||
newGroupName := "group_name123"
|
||||
err := auth.OnBoardGroup(&group, newGroupName)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to OnBoardGroup, %+v", group)
|
||||
}
|
||||
if group.GroupName != "group_name123" {
|
||||
t.Errorf("The OnBoardGroup should have name %v", newGroupName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticateHelperSearchUser(t *testing.T) {
|
||||
|
||||
user, err := auth.SearchUser("test")
|
||||
@ -307,3 +346,70 @@ func TestPostAuthentication(t *testing.T) {
|
||||
assert.EqualValues("test003@example.com", dbUser.Email)
|
||||
dao.CleanUser(int64(dbUser.UserID))
|
||||
}
|
||||
|
||||
func TestSearchAndOnBoardUser(t *testing.T) {
|
||||
userID, err := auth.SearchAndOnBoardUser("mike02")
|
||||
defer dao.CleanUser(int64(userID))
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred when SearchAndOnBoardUser: %v", err)
|
||||
}
|
||||
if userID == 0 {
|
||||
t.Errorf("Can not search and onboard user %v", "mike")
|
||||
}
|
||||
}
|
||||
func TestAddOrUpdateProjectMemberWithLdapUser(t *testing.T) {
|
||||
|
||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||
}
|
||||
member := models.MemberReq{
|
||||
ProjectID: currentProject.ProjectID,
|
||||
MemberUser: models.User{
|
||||
Username: "mike",
|
||||
},
|
||||
Role: models.PROJECTADMIN,
|
||||
}
|
||||
|
||||
pmid, err := api.AddOrUpdateProjectMember(currentProject.ProjectID, member)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
||||
}
|
||||
if pmid == 0 {
|
||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid:%v", pmid)
|
||||
}
|
||||
|
||||
}
|
||||
func TestAddProjectMemberWithLdapGroup(t *testing.T) {
|
||||
|
||||
currentProject, err := dao.GetProjectByName("member_test_01")
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||
}
|
||||
member := models.MemberReq{
|
||||
ProjectID: currentProject.ProjectID,
|
||||
MemberGroup: models.UserGroup{
|
||||
LdapGroupDN: "cn=harbor_users,ou=groups,dc=example,dc=com",
|
||||
},
|
||||
Role: models.PROJECTADMIN,
|
||||
}
|
||||
|
||||
pmid, err := api.AddOrUpdateProjectMember(currentProject.ProjectID, member)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: %v", err)
|
||||
}
|
||||
if pmid == 0 {
|
||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid: %v", pmid)
|
||||
}
|
||||
queryMember := models.Member{
|
||||
ProjectID: currentProject.ProjectID,
|
||||
}
|
||||
memberList, err := project.GetProjectMember(queryMember)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to query project member, %v, error: %v", queryMember, err)
|
||||
}
|
||||
|
||||
if len(memberList) == 0 {
|
||||
t.Errorf("Failed to query project member, %v", queryMember)
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ func initRouters() {
|
||||
|
||||
//API:
|
||||
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &api.ProjectUserMemberAPI{})
|
||||
beego.Router("/api/projects/:pid([0-9]+)/projectmembers/?:pmid([0-9]+)", &api.ProjectMemberAPI{})
|
||||
beego.Router("/api/projects/", &api.ProjectAPI{}, "head:Head")
|
||||
beego.Router("/api/projects/:id([0-9]+)", &api.ProjectAPI{})
|
||||
|
||||
@ -54,8 +55,10 @@ func initRouters() {
|
||||
beego.Router("/api/users", &api.UserAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/users/:id([0-9]+)/password", &api.UserAPI{}, "put:ChangePassword")
|
||||
beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole")
|
||||
beego.Router("/api/usergroups/?:ugid([0-9]+)", &api.UserGroupAPI{})
|
||||
beego.Router("/api/ldap/ping", &api.LdapAPI{}, "post:Ping")
|
||||
beego.Router("/api/ldap/users/search", &api.LdapAPI{}, "post:Search")
|
||||
beego.Router("/api/ldap/users/search", &api.LdapAPI{}, "get:Search")
|
||||
beego.Router("/api/ldap/groups/search", &api.LdapAPI{}, "get:SearchGroup")
|
||||
beego.Router("/api/ldap/users/import", &api.LdapAPI{}, "post:ImportUser")
|
||||
beego.Router("/api/email/ping", &api.EmailAPI{}, "post:Ping")
|
||||
}
|
||||
|
@ -36,6 +36,24 @@ member: cn=mike05,ou=people,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
objectclass: top
|
||||
|
||||
# Group Entry harbor_group
|
||||
dn: cn=harbor_group,ou=groups,dc=example,dc=com
|
||||
cn: harbor_group
|
||||
description: harbor group
|
||||
member: cn=mike,ou=people,dc=example,dc=com
|
||||
member: cn=mike02,ou=people,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
objectclass: top
|
||||
|
||||
# Group Entry harbor_group2
|
||||
dn: cn=harbor_group2,ou=groups,dc=example,dc=com
|
||||
cn: harbor_group2
|
||||
description: harbor group2
|
||||
member: cn=mike,ou=people,dc=example,dc=com
|
||||
member: cn=mike02,ou=people,dc=example,dc=com
|
||||
objectclass: groupOfNames
|
||||
objectclass: top
|
||||
|
||||
# User belongs to harbor_user
|
||||
dn: cn=mike,ou=people,dc=example,dc=com
|
||||
cn: mike
|
||||
|
Loading…
Reference in New Issue
Block a user