Merge pull request #4722 from stonezdj/security_context_for_group3

Update security context for assign role to project group member
This commit is contained in:
stone 2018-05-03 17:17:41 +08:00 committed by GitHub
commit e6a52f49a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 688 additions and 77 deletions

View File

@ -662,7 +662,7 @@ func TestGetTotalOfProjects(t *testing.T) {
func TestGetProjects(t *testing.T) {
projects, err := GetProjects(nil)
if err != nil {
t.Errorf("Error occurred in GetAllProjects: %v", err)
t.Errorf("Error occurred in GetProjects: %v", err)
}
if len(projects) != 2 {
t.Errorf("Expected length of projects is 2, but actual: %d, the projects: %+v", len(projects), projects)

View File

@ -15,8 +15,11 @@
package group
import (
"strings"
"time"
"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"
@ -133,3 +136,20 @@ func OnBoardUserGroup(g *models.UserGroup, keyAttribute string, combinedKeyAttri
return nil
}
// GetGroupDNQueryCondition get the part of IN ('XXX', 'XXX') condition
func GetGroupDNQueryCondition(userGroupList []*models.UserGroup) string {
result := make([]string, 0)
count := 0
for _, userGroup := range userGroupList {
if userGroup.GroupType == common.LdapGroupType {
result = append(result, "'"+userGroup.LdapGroupDN+"'")
count++
}
}
//No LDAP Group found
if count == 0 {
return ""
}
return strings.Join(result, ",")
}

View File

@ -21,6 +21,7 @@ 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"
)
@ -247,3 +248,178 @@ func TestOnBoardUserGroup(t *testing.T) {
})
}
}
func TestGetGroupDNQueryCondition(t *testing.T) {
userGroupList := []*models.UserGroup{
&models.UserGroup{
GroupName: "sample1",
GroupType: 1,
LdapGroupDN: "cn=sample1_users,ou=groups,dc=example,dc=com",
},
&models.UserGroup{
GroupName: "sample2",
GroupType: 1,
LdapGroupDN: "cn=sample2_users,ou=groups,dc=example,dc=com",
},
&models.UserGroup{
GroupName: "sample3",
GroupType: 0,
LdapGroupDN: "cn=sample3_users,ou=groups,dc=example,dc=com",
},
}
groupQueryConditions := GetGroupDNQueryCondition(userGroupList)
expectedConditions := `'cn=sample1_users,ou=groups,dc=example,dc=com','cn=sample2_users,ou=groups,dc=example,dc=com'`
if groupQueryConditions != expectedConditions {
t.Errorf("Failed to GetGroupDNQueryCondition, expected %v, actual %v", expectedConditions, groupQueryConditions)
}
var userGroupList2 []*models.UserGroup
groupQueryCondition2 := GetGroupDNQueryCondition(userGroupList2)
if len(groupQueryCondition2) > 0 {
t.Errorf("Failed to GetGroupDNQueryCondition, expected %v, actual %v", "", groupQueryCondition2)
}
groupQueryCondition3 := GetGroupDNQueryCondition(nil)
if len(groupQueryCondition3) > 0 {
t.Errorf("Failed to GetGroupDNQueryCondition, expected %v, actual %v", "", groupQueryCondition3)
}
}
func TestGetGroupProjects(t *testing.T) {
userID, err := dao.Register(models.User{
Username: "grouptestu09",
Email: "grouptest09@example.com",
Password: "Harbor123456",
})
defer dao.DeleteUser(int(userID))
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)
}
defer dao.DeleteProject(projectID2)
groupID, err := AddUserGroup(models.UserGroup{
GroupName: "test_group_01",
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)
type args struct {
groupDNCondition string
query *models.ProjectQueryParam
}
member := &models.MemberQuery{
Name: "grouptestu09",
}
tests := []struct {
name string
args args
wantSize int
wantErr bool
}{
{"Query with group DN",
args{"'cn=harbor_users,ou=groups,dc=example,dc=com'",
&models.ProjectQueryParam{
Member: member,
}},
1, false},
{"Query without group DN",
args{"",
&models.ProjectQueryParam{}},
1, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := dao.GetGroupProjects(tt.args.groupDNCondition, 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)
}
defer dao.DeleteProject(projectID2)
groupID, err := AddUserGroup(models.UserGroup{
GroupName: "test_group_01",
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)
type args struct {
groupDNCondition string
query *models.ProjectQueryParam
}
tests := []struct {
name string
args args
wantSize int
wantErr bool
}{
{"Query with group DN",
args{"'cn=harbor_users,ou=groups,dc=example,dc=com'",
&models.ProjectQueryParam{}},
1, false},
{"Query without group DN",
args{"",
&models.ProjectQueryParam{}},
1, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := dao.GetTotalGroupProjects(tt.args.groupDNCondition, 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)
}
})
}
}

View File

@ -135,42 +135,85 @@ func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
// GetProjects returns a project list according to the query conditions
func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
sql, params := projectQueryConditions(query)
if query == nil {
sql += ` order by p.name`
}
sqlStr, queryParam := projectQueryConditions(query)
sqlStr = `select distinct p.project_id, p.name, p.owner_id,
p.creation_time, p.update_time ` + sqlStr + ` order by p.name`
sqlStr, queryParam = CreatePagination(query, sqlStr, queryParam)
log.Debugf("sql:=%+v, param= %+v", sqlStr, queryParam)
var projects []*models.Project
_, err := GetOrmer().Raw(sqlStr, queryParam).QueryRows(&projects)
return projects, err
}
// GetGroupProjects - Get user's all projects, including user is the user member of this project
// and the user is in the group which is a group member of this project.
func GetGroupProjects(groupDNCondition string, query *models.ProjectQueryParam) ([]*models.Project, error) {
sql, params := projectQueryConditions(query)
sql = `select distinct p.project_id, p.name, p.owner_id,
p.creation_time, p.update_time ` + sql
if len(groupDNCondition) > 0 {
sql = fmt.Sprintf(
`%s union select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time
from project p
left join project_member pm on p.project_id = pm.project_id
left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g' and ug.group_type = 1
where ug.ldap_group_dn in ( %s ) order by name`,
sql, groupDNCondition)
}
sqlStr, queryParams := CreatePagination(query, sql, params)
log.Debugf("query sql:%v", sql)
var projects []*models.Project
_, err := GetOrmer().Raw(sql, params).QueryRows(&projects)
_, err := GetOrmer().Raw(sqlStr, queryParams).QueryRows(&projects)
return projects, err
}
// GetTotalGroupProjects - Get the total count of projects, including user is the member of this project and the
// user is in the group, which is the group member of this project.
func GetTotalGroupProjects(groupDNCondition string, query *models.ProjectQueryParam) (int, error) {
var sql string
sqlCondition, params := projectQueryConditions(query)
if len(groupDNCondition) == 0 {
sql = `select count(1) ` + sqlCondition
} else {
sql = fmt.Sprintf(
`select count(1)
from ( select p.project_id %s union select p.project_id
from project p
left join project_member pm on p.project_id = pm.project_id
left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g' and ug.group_type = 1
where ug.ldap_group_dn in ( %s )) t`,
sqlCondition, groupDNCondition)
}
log.Debugf("query sql:%v", sql)
var count int
if err := GetOrmer().Raw(sql, params).QueryRow(&count); err != nil {
return 0, err
}
return count, nil
}
func projectQueryConditions(query *models.ProjectQueryParam) (string, []interface{}) {
params := []interface{}{}
sql := ` from project as p`
if query == nil {
sql += ` where p.deleted=false`
return sql, params
}
// if query.ProjectIDs is not nil but has no element, the query will returns no rows
if query.ProjectIDs != nil && len(query.ProjectIDs) == 0 {
sql += ` where 1 = 0`
return sql, params
}
if len(query.Owner) != 0 {
sql += ` join harbor_user u1
on p.owner_id = u1.user_id`
}
if query.Member != nil && len(query.Member.Name) != 0 {
sql += ` join project_member pm
on p.project_id = pm.project_id
on p.project_id = pm.project_id and pm.entity_type = 'u'
join harbor_user u2
on pm.entity_id=u2.user_id`
}
@ -205,15 +248,18 @@ func projectQueryConditions(query *models.ProjectQueryParam) (string, []interfac
params = append(params, roleID)
}
}
if len(query.ProjectIDs) > 0 {
sql += fmt.Sprintf(` and p.project_id in ( %s )`,
paramPlaceholder(len(query.ProjectIDs)))
params = append(params, query.ProjectIDs)
}
return sql, params
}
if query.Pagination != nil && query.Pagination.Size > 0 {
sql += ` order by p.name limit ?`
// CreatePagination ...
func CreatePagination(query *models.ProjectQueryParam, sql string, params []interface{}) (string, []interface{}) {
if query != nil && query.Pagination != nil && query.Pagination.Size > 0 {
sql += ` limit ?`
params = append(params, query.Pagination.Size)
if query.Pagination.Page > 0 {
@ -221,7 +267,6 @@ func projectQueryConditions(query *models.ProjectQueryParam) (string, []interfac
params = append(params, (query.Pagination.Page-1)*query.Pagination.Size)
}
}
return sql, params
}
@ -231,12 +276,31 @@ func DeleteProject(id int64) error {
if err != nil {
return err
}
name := fmt.Sprintf("%s#%d", project.Name, project.ProjectID)
sql := `update project
set deleted = true, name = ?
where project_id = ?`
_, err = GetOrmer().Raw(sql, name, id).Exec()
return err
}
// GetRolesByLDAPGroup - Get Project roles of the
// specified group DN is a member of current project
func GetRolesByLDAPGroup(projectID int64, groupDNCondition string) ([]int, error) {
var roles []int
if len(groupDNCondition) == 0 {
return roles, nil
}
o := GetOrmer()
sql := fmt.Sprintf(
`select pm.role from project_member pm
left join user_group ug on pm.entity_type = 'g' and pm.entity_id = ug.id
where ug.ldap_group_dn in ( %s ) and pm.project_id = ? `,
groupDNCondition)
log.Debugf("sql:%v", sql)
if _, err := o.Raw(sql, projectID).QueryRows(&roles); err != nil {
log.Warningf("Error in GetRolesByLDAPGroup, error: %v", err)
return nil, err
}
return roles, nil
}

View File

@ -30,14 +30,14 @@ func GetProjectMember(queryMember models.Member) ([]*models.Member, error) {
}
o := dao.GetOrmer()
sql := ` select a.* from ((select pm.id as id, pm.project_id as project_id, ug.id as entity_id, ug.group_name as entity_name, ug.creation_time, ug.update_time, r.name as rolename,
sql := ` select a.* from (select pm.id as id, pm.project_id as project_id, ug.id as entity_id, ug.group_name as entity_name, ug.creation_time, ug.update_time, r.name as rolename,
r.role_id as role, pm.entity_type as entity_type from user_group ug join project_member pm
on pm.project_id = ? and ug.id = pm.entity_id join role r on pm.role = r.role_id where pm.entity_type = 'g')
on pm.project_id = ? and ug.id = pm.entity_id join role r on pm.role = r.role_id where pm.entity_type = 'g'
union
(select pm.id as id, pm.project_id as project_id, u.user_id as entity_id, u.username as entity_name, u.creation_time, u.update_time, r.name as rolename,
select pm.id as id, pm.project_id as project_id, u.user_id as entity_id, u.username as entity_name, u.creation_time, u.update_time, r.name as rolename,
r.role_id as role, pm.entity_type as entity_type from harbor_user u join project_member pm
on pm.project_id = ? and u.user_id = pm.entity_id
join role r on pm.role = r.role_id where u.deleted = false and pm.entity_type = 'u')) as a where a.project_id = ? `
join role r on pm.role = r.role_id where u.deleted = false and pm.entity_type = 'u') as a where a.project_id = ? `
queryParam := make([]interface{}, 1)
// used ProjectID already
@ -63,7 +63,7 @@ func GetProjectMember(queryMember models.Member) ([]*models.Member, error) {
sql += " and a.id = ? "
queryParam = append(queryParam, queryMember.ID)
}
sql += ` order by a.entity_name `
sql += ` order by entity_name `
members := []*models.Member{}
_, err := o.Raw(sql, queryParam).QueryRows(&members)
@ -120,29 +120,44 @@ func DeleteProjectMemberByID(pmid int) error {
// SearchMemberByName search members of the project by entity_name
func SearchMemberByName(projectID int64, entityName string) ([]*models.Member, error) {
o := dao.GetOrmer()
sql := `(select pm.id, pm.project_id,
sql := `select pm.id, pm.project_id,
u.username as entity_name,
r.name as rolename,
pm.role, pm.entity_id, pm.entity_type
from project_member pm
left join harbor_user u on pm.entity_id = u.user_id and pm.entity_type = 'u'
left join role r on pm.role = r.role_id
where u.deleted = false and pm.project_id = ? and u.username like ? order by entity_name )
where u.deleted = false and pm.project_id = ? and u.username like ?
union
(select pm.id, pm.project_id,
select pm.id, pm.project_id,
ug.group_name as entity_name,
r.name as rolename,
pm.role, pm.entity_id, pm.entity_type
from project_member pm
left join user_group ug on pm.entity_id = ug.id and pm.entity_type = 'g'
left join role r on pm.role = r.role_id
where pm.project_id = ? and ug.group_name like ? order by entity_name ) `
where pm.project_id = ? and ug.group_name like ?
order by entity_name `
queryParam := make([]interface{}, 4)
queryParam = append(queryParam, projectID)
queryParam = append(queryParam, "%"+dao.Escape(entityName)+"%")
queryParam = append(queryParam, projectID)
queryParam = append(queryParam, "%"+dao.Escape(entityName)+"%")
members := []*models.Member{}
log.Debugf("Query sql: %v", sql)
_, err := o.Raw(sql, queryParam).QueryRows(&members)
return members, err
}
// GetRolesByGroup -- Query group roles
func GetRolesByGroup(projectID int64, groupDNCondition string) []int {
var roles []int
o := dao.GetOrmer()
sql := `select role from project_member pm
left join user_group ug on pm.project_id = ?
where ug.group_type = 1 and ug.ldap_group_dn in (` + groupDNCondition + `)`
if _, err := o.Raw(sql, projectID).QueryRows(&roles); err != nil {
return roles
}
return roles
}

View File

@ -285,3 +285,50 @@ func TestGetProjectMember(t *testing.T) {
}
}
func PrepareGroupTest() {
initSqls := []string{
`insert into user_group (group_name, group_type, ldap_group_dn) values ('harbor_group_01', 1, 'cn=harbor_user,dc=example,dc=com')`,
`insert into harbor_user (username, email, password, realname) values ('sample01', 'sample01@example.com', 'harbor12345', 'sample01')`,
`insert into project (name, owner_id) values ('group_project', 1)`,
`insert into project (name, owner_id) values ('group_project_private', 1)`,
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project'), 'public', 'false')`,
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project_private'), 'public', 'false')`,
`insert into project_member (project_id, entity_id, entity_type, role) values ((select project_id from project where name = 'group_project'), (select id from user_group where group_name = 'harbor_group_01'),'g', 2)`,
}
clearSqls := []string{
`delete from project_metadata where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
`delete from project where name in ('group_project', 'group_project_private')`,
`delete from project_member where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
`delete from user_group where group_name = 'harbor_group_01'`,
`delete from harbor_user where username = 'sample01'`,
}
dao.PrepareTestData(clearSqls, initSqls)
}
func TestGetRolesByGroup(t *testing.T) {
PrepareGroupTest()
project, err := dao.GetProjectByName("group_project")
if err != nil {
t.Errorf("Error occurred when GetProjectByName : %v", err)
}
type args struct {
projectID int64
groupDNCondition string
}
tests := []struct {
name string
args args
want []int
}{
{"Query group with role", args{project.ProjectID, "'cn=harbor_user,dc=example,dc=com'"}, []int{2}},
{"Query group no role", args{project.ProjectID, "'cn=another_user,dc=example,dc=com'"}, []int{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetRolesByGroup(tt.args.projectID, tt.args.groupDNCondition); !dao.ArrayEqual(got, tt.want) {
t.Errorf("GetRolesByGroup() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -103,9 +103,9 @@ func Test_projectQueryConditions(t *testing.T) {
args{query: &models.ProjectQueryParam{ProjectIDs: []int64{2, 3}, Owner: "admin", Name: "sample", Member: &models.MemberQuery{Name: "name", Role: 1}, Pagination: &models.Pagination{Page: 1, Size: 20}}},
` from project as p join harbor_user u1
on p.owner_id = u1.user_id join project_member pm
on p.project_id = pm.project_id
on p.project_id = pm.project_id and pm.entity_type = 'u'
join harbor_user u2
on pm.entity_id=u2.user_id where p.deleted=false and u1.username=? and p.name like ? and u2.username=? and pm.role = ? and p.project_id in ( ?,? ) order by p.name limit ? offset ?`,
on pm.entity_id=u2.user_id where p.deleted=false and u1.username=? and p.name like ? and u2.username=? and pm.role = ? and p.project_id in ( ?,? )`,
[]interface{}{1, []int64{2, 3}, 20, 0}},
}
for _, tt := range tests {
@ -117,3 +117,121 @@ func Test_projectQueryConditions(t *testing.T) {
})
}
}
func TestGetGroupProjects(t *testing.T) {
prepareGroupTest()
query := &models.ProjectQueryParam{Member: &models.MemberQuery{Name: "sample_group"}}
type args struct {
groupDNCondition string
query *models.ProjectQueryParam
}
tests := []struct {
name string
args args
wantSize int
wantErr bool
}{
{"Verify correct sql", args{groupDNCondition: "'cn=harbor_user,dc=example,dc=com'", query: query}, 1, false},
{"Verify missed sql", args{groupDNCondition: "'cn=another_user,dc=example,dc=com'", query: query}, 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetGroupProjects(tt.args.groupDNCondition, 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() = %v, want %v", got, tt.wantSize)
}
})
}
}
func prepareGroupTest() {
initSqls := []string{
`insert into user_group (group_name, group_type, ldap_group_dn) values ('harbor_group_01', 1, 'cn=harbor_user,dc=example,dc=com')`,
`insert into harbor_user (username, email, password, realname) values ('sample01', 'sample01@example.com', 'harbor12345', 'sample01')`,
`insert into project (name, owner_id) values ('group_project', 1)`,
`insert into project (name, owner_id) values ('group_project_private', 1)`,
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project'), 'public', 'false')`,
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project_private'), 'public', 'false')`,
`insert into project_member (project_id, entity_id, entity_type, role) values ((select project_id from project where name = 'group_project'), (select id from user_group where group_name = 'harbor_group_01'),'g', 2)`,
}
clearSqls := []string{
`delete from project_metadata where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
`delete from project where name in ('group_project', 'group_project_private')`,
`delete from project_member where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
`delete from user_group where group_name = 'harbor_group_01'`,
`delete from harbor_user where username = 'sample01'`,
}
PrepareTestData(clearSqls, initSqls)
}
func TestGetTotalGroupProjects(t *testing.T) {
prepareGroupTest()
query := &models.ProjectQueryParam{Member: &models.MemberQuery{Name: "sample_group"}}
type args struct {
groupDNCondition string
query *models.ProjectQueryParam
}
tests := []struct {
name string
args args
want int
wantErr bool
}{
{"Verify correct sql", args{groupDNCondition: "'cn=harbor_user,dc=example,dc=com'", query: query}, 1, false},
{"Verify missed sql", args{groupDNCondition: "'cn=another_user,dc=example,dc=com'", query: query}, 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetTotalGroupProjects(tt.args.groupDNCondition, tt.args.query)
if (err != nil) != tt.wantErr {
t.Errorf("GetTotalGroupProjects() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GetTotalGroupProjects() = %v, want %v", got, tt.want)
}
})
}
}
func TestGetRolesByLDAPGroup(t *testing.T) {
prepareGroupTest()
project, err := GetProjectByName("group_project")
if err != nil {
t.Errorf("Error occurred when Get project by name: %v", err)
}
privateProject, err := GetProjectByName("group_project_private")
if err != nil {
t.Errorf("Error occurred when Get project by name: %v", err)
}
type args struct {
projectID int64
groupDNCondition string
}
tests := []struct {
name string
args args
wantSize int
wantErr bool
}{
{"Check normal", args{project.ProjectID, "'cn=harbor_user,dc=example,dc=com'"}, 1, false},
{"Check non exist", args{privateProject.ProjectID, "'cn=not_harbor_user,dc=example,dc=com'"}, 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetRolesByLDAPGroup(tt.args.projectID, tt.args.groupDNCondition)
if (err != nil) != tt.wantErr {
t.Errorf("TestGetRolesByLDAPGroup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.wantSize {
t.Errorf("TestGetRolesByLDAPGroup() = %v, want %v", len(got), tt.wantSize)
}
})
}
}

View File

@ -116,3 +116,17 @@ func PrepareTestData(clearSqls []string, initSqls []string) {
}
}
}
// ArrayEqual ...
func ArrayEqual(arrayA, arrayB []int) bool {
if len(arrayA) != len(arrayB) {
return false
}
size := len(arrayA)
for i := 0; i < size; i++ {
if arrayA[i] != arrayB[i] {
return false
}
}
return true
}

View File

@ -147,8 +147,9 @@ type ProjectQueryParam struct {
// MemberQuery fitler by member's username and role
type MemberQuery struct {
Name string // the username of member
Role int // the role of the member has to the project
Name string // the username of member
Role int // the role of the member has to the project
GroupList []*UserGroup // the group list of current user
}
// Pagination ...

View File

@ -35,11 +35,12 @@ type User struct {
//to it.
Role int `orm:"-" json:"role_id"`
// RoleList []Role `json:"role_list"`
HasAdminRole bool `orm:"column(sysadmin_flag)" json:"has_admin_role"`
ResetUUID string `orm:"column(reset_uuid)" json:"reset_uuid"`
Salt string `orm:"column(salt)" json:"-"`
CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
UpdateTime time.Time `orm:"column(update_time)" json:"update_time"`
HasAdminRole bool `orm:"column(sysadmin_flag)" json:"has_admin_role"`
ResetUUID string `orm:"column(reset_uuid)" json:"reset_uuid"`
Salt string `orm:"column(salt)" json:"-"`
CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
UpdateTime time.Time `orm:"column(update_time)" json:"update_time"`
GroupList []*UserGroup `orm:"-" json:"-"`
}
// UserQuery ...

View File

@ -13,5 +13,3 @@
// limitations under the License.
package admiral
// TODO add test cases

View File

@ -34,6 +34,8 @@ type Context interface {
HasWritePerm(projectIDOrName interface{}) bool
// HasAllPerm returns whether the user has all permissions to the project
HasAllPerm(projectIDOrName interface{}) bool
//Get current user's all project
GetMyProjects() ([]*models.Project, error)
//Get user's role in provided project
GetProjectRoles(projectIDOrName interface{}) []int
}

View File

@ -17,6 +17,7 @@ package local
import (
"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"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/promgr"
@ -88,7 +89,6 @@ func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
}
roles := s.GetProjectRoles(projectIDOrName)
return len(roles) > 0
}
@ -97,12 +97,10 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
if !s.IsAuthenticated() {
return false
}
// system admin
if s.IsSysAdmin() {
return true
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
@ -111,7 +109,6 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
return true
}
}
return false
}
@ -120,12 +117,10 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
if !s.IsAuthenticated() {
return false
}
// system admin
if s.IsSysAdmin() {
return true
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
@ -133,19 +128,9 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return true
}
}
return false
}
// GetMyProjects ...
func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
return dao.GetProjects(&models.ProjectQueryParam{
Member: &models.MemberQuery{
Name: s.GetUsername(),
},
})
}
// GetProjectRoles ...
func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
if !s.IsAuthenticated() || projectIDOrName == nil {
@ -164,7 +149,6 @@ func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
log.Debugf("user %s not found", s.GetUsername())
return roles
}
project, err := s.pm.Get(projectIDOrName)
if err != nil {
log.Errorf("failed to get project %v: %v", projectIDOrName, err)
@ -174,13 +158,11 @@ func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
log.Errorf("project %v not found", projectIDOrName)
return roles
}
roleList, err := dao.GetUserProjectRoles(user.UserID, project.ProjectID, common.UserMember)
if err != nil {
log.Errorf("failed to get roles of user %d to project %d: %v", user.UserID, project.ProjectID, err)
return roles
}
for _, role := range roleList {
switch role.RoleCode {
case "MDRWS":
@ -191,8 +173,42 @@ func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
roles = append(roles, common.RoleGuest)
}
}
if len(roles) != 0 {
return roles
}
return s.GetRolesByGroup(projectIDOrName)
}
//If len(roles)==0, Get Group Roles
// GetRolesByGroup - Get the group role of current user to the project
func (s *SecurityContext) GetRolesByGroup(projectIDOrName interface{}) []int {
var roles []int
user := s.user
project, err := s.pm.Get(projectIDOrName)
//No user, group or project info
if err != nil || project == nil || user == nil || len(user.GroupList) == 0 {
return roles
}
//Get role by LDAP group
groupDNConditions := group.GetGroupDNQueryCondition(user.GroupList)
roles, err = dao.GetRolesByLDAPGroup(project.ProjectID, groupDNConditions)
if err != nil {
return nil
}
return roles
}
// GetMyProjects ...
func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
result, err := s.pm.List(
&models.ProjectQueryParam{
Member: &models.MemberQuery{
Name: s.GetUsername(),
GroupList: s.user.GroupList,
},
})
if err != nil {
return nil, err
}
return result.Projects, nil
}

View File

@ -272,6 +272,25 @@ func TestHasAllPerm(t *testing.T) {
assert.True(t, ctx.HasAllPerm(private.Name))
}
func TestHasAllPermWithGroup(t *testing.T) {
PrepareGroupTest()
project, err := dao.GetProjectByName("group_project")
if err != nil {
t.Errorf("Error occurred when GetProjectByName: %v", err)
}
developer, err := dao.GetUser(models.User{Username: "sample01"})
if err != nil {
t.Errorf("Error occurred when GetUser: %v", err)
}
developer.GroupList = []*models.UserGroup{
&models.UserGroup{GroupName: "test_group", GroupType: 1, LdapGroupDN: "cn=harbor_user,dc=example,dc=com"},
}
ctx := NewSecurityContext(developer, pm)
assert.False(t, ctx.HasAllPerm(project.Name))
assert.True(t, ctx.HasWritePerm(project.Name))
assert.True(t, ctx.HasReadPerm(project.Name))
}
func TestGetMyProjects(t *testing.T) {
ctx := NewSecurityContext(guestUser, pm)
projects, err := ctx.GetMyProjects()
@ -309,3 +328,96 @@ func TestGetProjectRoles(t *testing.T) {
assert.Equal(t, 1, len(roles))
assert.Equal(t, common.RoleProjectAdmin, roles[0])
}
func PrepareGroupTest() {
initSqls := []string{
`insert into user_group (group_name, group_type, ldap_group_dn) values ('harbor_group_01', 1, 'cn=harbor_user,dc=example,dc=com')`,
`insert into harbor_user (username, email, password, realname) values ('sample01', 'sample01@example.com', 'harbor12345', 'sample01')`,
`insert into project (name, owner_id) values ('group_project', 1)`,
`insert into project (name, owner_id) values ('group_project_private', 1)`,
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project'), 'public', 'false')`,
`insert into project_metadata (project_id, name, value) values ((select project_id from project where name = 'group_project_private'), 'public', 'false')`,
`insert into project_member (project_id, entity_id, entity_type, role) values ((select project_id from project where name = 'group_project'), (select id from user_group where group_name = 'harbor_group_01'),'g', 2)`,
}
clearSqls := []string{
`delete from project_metadata where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
`delete from project where name in ('group_project', 'group_project_private')`,
`delete from project_member where project_id in (select project_id from project where name in ('group_project', 'group_project_private'))`,
`delete from user_group where group_name = 'harbor_group_01'`,
`delete from harbor_user where username = 'sample01'`,
}
dao.PrepareTestData(clearSqls, initSqls)
}
func TestSecurityContext_GetRolesByGroup(t *testing.T) {
PrepareGroupTest()
project, err := dao.GetProjectByName("group_project")
if err != nil {
t.Errorf("Error occurred when GetProjectByName: %v", err)
}
developer, err := dao.GetUser(models.User{Username: "sample01"})
if err != nil {
t.Errorf("Error occurred when GetUser: %v", err)
}
developer.GroupList = []*models.UserGroup{
&models.UserGroup{GroupName: "test_group", GroupType: 1, LdapGroupDN: "cn=harbor_user,dc=example,dc=com"},
}
type fields struct {
user *models.User
pm promgr.ProjectManager
}
type args struct {
projectIDOrName interface{}
}
tests := []struct {
name string
fields fields
args args
want []int
}{
{"Developer", fields{user: developer, pm: pm}, args{project.ProjectID}, []int{2}},
{"Guest", fields{user: guestUser, pm: pm}, args{project.ProjectID}, []int{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &SecurityContext{
user: tt.fields.user,
pm: tt.fields.pm,
}
if got := s.GetRolesByGroup(tt.args.projectIDOrName); !dao.ArrayEqual(got, tt.want) {
t.Errorf("SecurityContext.GetRolesByGroup() = %v, want %v", got, tt.want)
}
})
}
}
func TestSecurityContext_GetMyProjects(t *testing.T) {
type fields struct {
user *models.User
pm promgr.ProjectManager
}
tests := []struct {
name string
fields fields
wantSize int
wantErr bool
}{
{"Admin", fields{user: projectAdminUser, pm: pm}, 1, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &SecurityContext{
user: tt.fields.user,
pm: tt.fields.pm,
}
got, err := s.GetMyProjects()
if (err != nil) != tt.wantErr {
t.Errorf("SecurityContext.GetMyProjects() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.wantSize {
t.Errorf("SecurityContext.GetMyProjects() = %v, want %v", len(got), tt.wantSize)
}
})
}
}

View File

@ -347,19 +347,16 @@ func (p *ProjectAPI) List() {
return
}
projects = append(projects, pros...)
mps, err := p.ProjectMgr.List(&models.ProjectQueryParam{
Member: &models.MemberQuery{
Name: p.SecurityCtx.GetUsername(),
},
})
mps, err := p.SecurityCtx.GetMyProjects()
if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to list projects: %v", err))
return
}
projects = append(projects, mps.Projects...)
projects = append(projects, mps...)
}
}
//Query projects by user group
if projects != nil {
projectIDs := []int64{}
for _, project := range projects {

View File

@ -76,14 +76,31 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
u.Username = ldapUsers[0].Username
u.Email = strings.TrimSpace(ldapUsers[0].Email)
u.Realname = ldapUsers[0].Realname
userGroups := make([]*models.UserGroup, 0)
dn := ldapUsers[0].DN
if err = ldapSession.Bind(dn, m.Password); err != nil {
log.Warningf("Failed to bind user, username: %s, dn: %s, error: %v", u.Username, dn, err)
return nil, auth.NewErrAuth(err.Error())
}
//Attach user group
for _, groupDN := range ldapUsers[0].GroupDNList {
userGroupQuery := models.UserGroup{
GroupType: 1,
LdapGroupDN: groupDN,
}
userGroupList, err := group.QueryUserGroup(userGroupQuery)
if err != nil {
continue
}
if len(userGroupList) == 0 {
continue
}
userGroups = append(userGroups, userGroupList[0])
}
u.GroupList = userGroups
return &u, nil
}

View File

@ -109,7 +109,7 @@ func TestMain(m *testing.M) {
//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 harbor_user (username, email, password, realname) values ('member_test_01', 'member_test_01@example.com', '123456', 'member_test_01')",
"insert into project (name, owner_id) values ('member_test_01', 1)",
"insert into user_group (group_name, group_type, 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 harbor_user where username = 'member_test_01') where name = 'member_test_01'",

View File

@ -20,6 +20,7 @@ import (
"time"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/dao/group"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils"
errutil "github.com/vmware/harbor/src/common/utils/error"
@ -117,7 +118,6 @@ func (d *driver) Delete(projectIDOrName interface{}) error {
}
id = project.ProjectID
}
return dao.DeleteProject(id)
}
@ -129,17 +129,26 @@ func (d *driver) Update(projectIDOrName interface{},
}
// List returns a project list according to the query parameters
func (d *driver) List(query *models.ProjectQueryParam) (
*models.ProjectQueryResult, error) {
total, err := dao.GetTotalOfProjects(query)
func (d *driver) List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error) {
var total int64
var projects []*models.Project
var groupDNCondition string
//List with LDAP group projects
if query != nil && query.Member != nil {
groupDNCondition = group.GetGroupDNQueryCondition(query.Member.GroupList)
}
count, err := dao.GetTotalGroupProjects(groupDNCondition, query)
if err != nil {
return nil, err
}
total = int64(count)
projects, err = dao.GetGroupProjects(groupDNCondition, query)
if err != nil {
return nil, err
}
projects, err := dao.GetProjects(query)
if err != nil {
return nil, err
}
return &models.ProjectQueryResult{
Total: total,
Projects: projects,

View File

@ -7,7 +7,11 @@ cp make/common/config/ui/private_key.pem /etc/ui/
mkdir src/ui/conf
cp make/common/config/ui/app.conf src/ui/conf/
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
if [ "$(uname)" == "Darwin" ]; then
IP=`ifconfig en0 | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}'`
else
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
fi
echo "server ip is "$IP
sed -i -r "s/POSTGRESQL_HOST=postgresql/POSTGRESQL_HOST=$IP/" make/common/config/adminserver/env
sed -i -r "s|REGISTRY_URL=http://registry:5000|REGISTRY_URL=http://$IP:5000|" make/common/config/adminserver/env