mirror of
https://github.com/goharbor/harbor
synced 2025-04-15 21:35:53 +00:00
411 lines
12 KiB
Go
411 lines
12 KiB
Go
// Copyright Project Harbor Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package local
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/goharbor/harbor/src/common"
|
|
"github.com/goharbor/harbor/src/common/dao"
|
|
"github.com/goharbor/harbor/src/common/dao/group"
|
|
"github.com/goharbor/harbor/src/common/dao/project"
|
|
"github.com/goharbor/harbor/src/common/models"
|
|
"github.com/goharbor/harbor/src/common/rbac"
|
|
"github.com/goharbor/harbor/src/common/utils/log"
|
|
"github.com/goharbor/harbor/src/common/utils/test"
|
|
"github.com/goharbor/harbor/src/core/promgr"
|
|
"github.com/goharbor/harbor/src/core/promgr/pmsdriver/local"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
private = &models.Project{
|
|
Name: "private_project",
|
|
OwnerID: 1,
|
|
}
|
|
|
|
projectAdminUser = &models.User{
|
|
Username: "projectAdminUser",
|
|
Email: "projectAdminUser@vmware.com",
|
|
}
|
|
developerUser = &models.User{
|
|
Username: "developerUser",
|
|
Email: "developerUser@vmware.com",
|
|
}
|
|
guestUser = &models.User{
|
|
Username: "guestUser",
|
|
Email: "guestUser@vmware.com",
|
|
}
|
|
|
|
pm = promgr.NewDefaultProjectManager(local.NewDriver(), true)
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
test.InitDatabaseFromEnv()
|
|
|
|
// regiser users
|
|
id, err := dao.Register(*projectAdminUser)
|
|
if err != nil {
|
|
log.Fatalf("failed to register user: %v", err)
|
|
}
|
|
projectAdminUser.UserID = int(id)
|
|
defer dao.DeleteUser(int(id))
|
|
|
|
id, err = dao.Register(*developerUser)
|
|
if err != nil {
|
|
log.Fatalf("failed to register user: %v", err)
|
|
}
|
|
developerUser.UserID = int(id)
|
|
defer dao.DeleteUser(int(id))
|
|
|
|
id, err = dao.Register(*guestUser)
|
|
if err != nil {
|
|
log.Fatalf("failed to register user: %v", err)
|
|
}
|
|
guestUser.UserID = int(id)
|
|
defer dao.DeleteUser(int(id))
|
|
|
|
// add project
|
|
id, err = dao.AddProject(*private)
|
|
if err != nil {
|
|
log.Fatalf("failed to add project: %v", err)
|
|
}
|
|
private.ProjectID = id
|
|
defer dao.DeleteProject(id)
|
|
|
|
var projectAdminPMID, developerUserPMID, guestUserPMID int
|
|
// add project members
|
|
projectAdminPMID, err = project.AddProjectMember(models.Member{
|
|
ProjectID: private.ProjectID,
|
|
EntityID: projectAdminUser.UserID,
|
|
EntityType: common.UserMember,
|
|
Role: common.RoleProjectAdmin,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("failed to add member: %v", err)
|
|
}
|
|
defer project.DeleteProjectMemberByID(projectAdminPMID)
|
|
|
|
developerUserPMID, err = project.AddProjectMember(models.Member{
|
|
ProjectID: private.ProjectID,
|
|
EntityID: developerUser.UserID,
|
|
EntityType: common.UserMember,
|
|
Role: common.RoleDeveloper,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("failed to add member: %v", err)
|
|
}
|
|
defer project.DeleteProjectMemberByID(developerUserPMID)
|
|
guestUserPMID, err = project.AddProjectMember(models.Member{
|
|
ProjectID: private.ProjectID,
|
|
EntityID: guestUser.UserID,
|
|
EntityType: common.UserMember,
|
|
Role: common.RoleGuest,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("failed to add member: %v", err)
|
|
}
|
|
defer project.DeleteProjectMemberByID(guestUserPMID)
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func TestIsAuthenticated(t *testing.T) {
|
|
// unauthenticated
|
|
ctx := NewSecurityContext(nil, nil)
|
|
assert.False(t, ctx.IsAuthenticated())
|
|
|
|
// authenticated
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "test",
|
|
}, nil)
|
|
assert.True(t, ctx.IsAuthenticated())
|
|
}
|
|
|
|
func TestGetUsername(t *testing.T) {
|
|
// unauthenticated
|
|
ctx := NewSecurityContext(nil, nil)
|
|
assert.Equal(t, "", ctx.GetUsername())
|
|
|
|
// authenticated
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "test",
|
|
}, nil)
|
|
assert.Equal(t, "test", ctx.GetUsername())
|
|
}
|
|
|
|
func TestIsSysAdmin(t *testing.T) {
|
|
// unauthenticated
|
|
ctx := NewSecurityContext(nil, nil)
|
|
assert.False(t, ctx.IsSysAdmin())
|
|
|
|
// authenticated, non admin
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "test",
|
|
}, nil)
|
|
assert.False(t, ctx.IsSysAdmin())
|
|
|
|
// authenticated, admin
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "test",
|
|
HasAdminRole: true,
|
|
}, nil)
|
|
assert.True(t, ctx.IsSysAdmin())
|
|
}
|
|
|
|
func TestIsSolutionUser(t *testing.T) {
|
|
ctx := NewSecurityContext(nil, nil)
|
|
assert.False(t, ctx.IsSolutionUser())
|
|
}
|
|
|
|
func TestHasPullPerm(t *testing.T) {
|
|
// public project
|
|
ctx := NewSecurityContext(nil, pm)
|
|
|
|
resource := rbac.NewProjectNamespace("library").Resource(rbac.ResourceRepository)
|
|
assert.True(t, ctx.Can(rbac.ActionPull, resource))
|
|
|
|
// private project, unauthenticated
|
|
ctx = NewSecurityContext(nil, pm)
|
|
resource = rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
|
|
assert.False(t, ctx.Can(rbac.ActionPull, resource))
|
|
|
|
// private project, authenticated, has no perm
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "test",
|
|
}, pm)
|
|
assert.False(t, ctx.Can(rbac.ActionPull, resource))
|
|
|
|
// private project, authenticated, has read perm
|
|
ctx = NewSecurityContext(guestUser, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPull, resource))
|
|
|
|
// private project, authenticated, system admin
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "admin",
|
|
HasAdminRole: true,
|
|
}, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPull, resource))
|
|
}
|
|
|
|
func TestHasPushPerm(t *testing.T) {
|
|
resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
|
|
|
|
// unauthenticated
|
|
ctx := NewSecurityContext(nil, pm)
|
|
assert.False(t, ctx.Can(rbac.ActionPush, resource))
|
|
|
|
// authenticated, has read perm
|
|
ctx = NewSecurityContext(guestUser, pm)
|
|
assert.False(t, ctx.Can(rbac.ActionPush, resource))
|
|
|
|
// authenticated, has write perm
|
|
ctx = NewSecurityContext(developerUser, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPush, resource))
|
|
|
|
// authenticated, system admin
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "admin",
|
|
HasAdminRole: true,
|
|
}, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPush, resource))
|
|
}
|
|
|
|
func TestHasPushPullPerm(t *testing.T) {
|
|
resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
|
|
|
|
// unauthenticated
|
|
ctx := NewSecurityContext(nil, pm)
|
|
assert.False(t, ctx.Can(rbac.ActionPush, resource) && ctx.Can(rbac.ActionPull, resource))
|
|
|
|
// authenticated, has all perms
|
|
ctx = NewSecurityContext(projectAdminUser, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPush, resource) && ctx.Can(rbac.ActionPull, resource))
|
|
|
|
// authenticated, system admin
|
|
ctx = NewSecurityContext(&models.User{
|
|
Username: "admin",
|
|
HasAdminRole: true,
|
|
}, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPush, resource) && ctx.Can(rbac.ActionPull, resource))
|
|
}
|
|
|
|
func TestHasPushPullPermWithGroup(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)
|
|
}
|
|
|
|
userGroups, err := group.QueryUserGroup(models.UserGroup{GroupType: common.LDAPGroupType, LdapGroupDN: "cn=harbor_user,dc=example,dc=com"})
|
|
if err != nil {
|
|
t.Errorf("Failed to query user group %v", err)
|
|
}
|
|
if len(userGroups) < 1 {
|
|
t.Errorf("Failed to retrieve user group")
|
|
}
|
|
|
|
developer.GroupIDs = []int{userGroups[0].ID}
|
|
|
|
resource := rbac.NewProjectNamespace(project.Name).Resource(rbac.ResourceRepository)
|
|
|
|
ctx := NewSecurityContext(developer, pm)
|
|
assert.True(t, ctx.Can(rbac.ActionPush, resource))
|
|
assert.True(t, ctx.Can(rbac.ActionPull, resource))
|
|
}
|
|
|
|
func TestGetMyProjects(t *testing.T) {
|
|
ctx := NewSecurityContext(guestUser, pm)
|
|
projects, err := ctx.GetMyProjects()
|
|
require.Nil(t, err)
|
|
assert.Equal(t, 1, len(projects))
|
|
assert.Equal(t, private.ProjectID, projects[0].ProjectID)
|
|
}
|
|
|
|
func TestGetProjectRoles(t *testing.T) {
|
|
// unauthenticated
|
|
ctx := NewSecurityContext(nil, pm)
|
|
roles := ctx.GetProjectRoles(private.Name)
|
|
assert.Equal(t, 0, len(roles))
|
|
|
|
// authenticated, project name of ID is nil
|
|
ctx = NewSecurityContext(guestUser, pm)
|
|
roles = ctx.GetProjectRoles(nil)
|
|
assert.Equal(t, 0, len(roles))
|
|
|
|
// authenticated, has read perm
|
|
ctx = NewSecurityContext(guestUser, pm)
|
|
roles = ctx.GetProjectRoles(private.Name)
|
|
assert.Equal(t, 1, len(roles))
|
|
assert.Equal(t, common.RoleGuest, roles[0])
|
|
|
|
// authenticated, has write perm
|
|
ctx = NewSecurityContext(developerUser, pm)
|
|
roles = ctx.GetProjectRoles(private.Name)
|
|
assert.Equal(t, 1, len(roles))
|
|
assert.Equal(t, common.RoleDeveloper, roles[0])
|
|
|
|
// authenticated, has all perms
|
|
ctx = NewSecurityContext(projectAdminUser, pm)
|
|
roles = ctx.GetProjectRoles(private.Name)
|
|
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)
|
|
}
|
|
userGroups, err := group.QueryUserGroup(models.UserGroup{GroupType: common.LDAPGroupType, LdapGroupDN: "cn=harbor_user,dc=example,dc=com"})
|
|
if err != nil {
|
|
t.Errorf("Failed to query user group %v", err)
|
|
}
|
|
if len(userGroups) < 1 {
|
|
t.Errorf("Failed to retrieve user group")
|
|
}
|
|
|
|
developer.GroupIDs = []int{userGroups[0].ID}
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|