Move some method of project manager to security context

This commit is contained in:
Wenkai Yin 2017-07-05 16:06:58 +08:00
parent b51b3ea5ac
commit b866200e4f
18 changed files with 458 additions and 585 deletions

View File

@ -16,6 +16,7 @@ package admiral
import (
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/security/authcontext"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/projectmanager"
@ -41,7 +42,7 @@ func (s *SecurityContext) IsAuthenticated() bool {
if s.ctx == nil {
return false
}
return len(s.ctx.GetUsername()) > 0
return len(s.ctx.PrincipalID) > 0
}
// GetUsername returns the username of the authenticated user
@ -50,7 +51,7 @@ func (s *SecurityContext) GetUsername() string {
if !s.IsAuthenticated() {
return ""
}
return s.ctx.GetUsername()
return s.ctx.PrincipalID
}
// IsSysAdmin returns whether the authenticated user is system admin
@ -59,12 +60,12 @@ func (s *SecurityContext) IsSysAdmin() bool {
if !s.IsAuthenticated() {
return false
}
return s.ctx.IsSysAdmin()
}
// HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
// public project
public, err := s.pm.IsPublic(projectIDOrName)
if err != nil {
log.Errorf("failed to check the public of project %v: %v",
@ -85,27 +86,9 @@ func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
return true
}
if name, ok := projectIDOrName.(string); ok {
return s.ctx.HasReadPerm(name)
}
roles := s.GetProjectRoles(projectIDOrName)
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
if err != nil {
log.Errorf("failed to get roles of user %s to project %v: %v",
s.GetUsername(), projectIDOrName, err)
return false
}
for _, role := range roles {
switch role {
case common.RoleProjectAdmin,
common.RoleDeveloper,
common.RoleGuest:
return true
}
}
return false
return len(roles) > 0
}
// HasWritePerm returns whether the user has write permission to the project
@ -119,17 +102,7 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
return true
}
if name, ok := projectIDOrName.(string); ok {
return s.ctx.HasWritePerm(name)
}
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
if err != nil {
log.Errorf("failed to get roles of user %s to project %v: %v",
s.GetUsername(), projectIDOrName, err)
return false
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin,
@ -152,17 +125,7 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return true
}
if name, ok := projectIDOrName.(string); ok {
return s.ctx.HasAllPerm(name)
}
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
if err != nil {
log.Errorf("failed to get roles of user %s to project %v: %v",
s.GetUsername(), projectIDOrName, err)
return false
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin:
@ -172,3 +135,17 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return false
}
// GetMyProjects ...
func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
return s.ctx.GetMyProjects(), nil
}
// GetProjectRoles ...
func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
if !s.IsAuthenticated() || projectIDOrName == nil {
return []int{}
}
return s.ctx.GetProjectRoles(projectIDOrName)
}

View File

@ -21,9 +21,12 @@ import (
"io/ioutil"
"net/http"
"strings"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
)
// TODO update the value of role when admiral API is ready
const (
// AuthTokenHeader is the key of auth token header
AuthTokenHeader = "x-xenon-auth-token"
@ -48,70 +51,83 @@ type AuthContext struct {
Projects []*project `json:"projects"`
}
// GetUsername ...
func (a *AuthContext) GetUsername() string {
return a.PrincipalID
}
// IsSysAdmin ...
func (a *AuthContext) IsSysAdmin() bool {
isSysAdmin := false
for _, role := range a.Roles {
if role == sysAdminRole {
isSysAdmin = true
break
}
}
return isSysAdmin
}
// HasReadPerm ...
func (a *AuthContext) HasReadPerm(projectName string) bool {
roles := a.getRoles(projectName)
return len(roles) > 0
}
// HasWritePerm ...
func (a *AuthContext) HasWritePerm(projectName string) bool {
roles := a.getRoles(projectName)
for _, role := range roles {
if role == projectAdminRole || role == developerRole {
return true
}
}
return false
}
// HasAllPerm ...
func (a *AuthContext) HasAllPerm(projectName string) bool {
roles := a.getRoles(projectName)
for _, role := range roles {
if role == projectAdminRole {
return true
}
}
return false
}
// GetProjectRoles ...
func (a *AuthContext) GetProjectRoles(projectIDOrName interface{}) []int {
var isID bool
var id int64
var name string
func (a *AuthContext) getRoles(projectName string) []string {
id, isID = projectIDOrName.(int64)
if !isID {
name, _ = projectIDOrName.(string)
}
roles := []string{}
for _, project := range a.Projects {
if project.Name == projectName {
return project.Roles
p := convertProject(project)
if isID {
if p.ProjectID == id {
roles = append(roles, project.Roles...)
break
}
} else {
if p.Name == name {
roles = append(roles, project.Roles...)
break
}
}
}
return []string{}
return convertRoles(roles)
}
// GetMyProjects returns all projects which the user is a member of
func (a *AuthContext) GetMyProjects() []string {
projects := []string{}
func (a *AuthContext) GetMyProjects() []*models.Project {
projects := []*models.Project{}
for _, project := range a.Projects {
projects = append(projects, project.Name)
projects = append(projects, convertProject(project))
}
return projects
}
// TODO populate harbor ID to the project
// convert project returned by Admiral to project used in Harbor
func convertProject(p *project) *models.Project {
project := &models.Project{
Name: p.Name,
}
return project
}
// convert roles defined by Admiral to roles used in Harbor
func convertRoles(roles []string) []int {
list := []int{}
for _, role := range roles {
switch role {
case projectAdminRole:
list = append(list, common.RoleProjectAdmin)
case developerRole:
list = append(list, common.RoleDeveloper)
case guestRole:
list = append(list, common.RoleGuest)
default:
log.Warningf("unknow role: %s", role)
}
}
return list
}
// GetAuthCtx returns the auth context of the current user
func GetAuthCtx(client *http.Client, url, token string) (*AuthContext, error) {
return get(client, url, token)

View File

@ -14,4 +14,61 @@
package authcontext
// TODO add test cases
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsSysAdmin(t *testing.T) {
// nil roles
ctx := &AuthContext{}
assert.False(t, ctx.IsSysAdmin())
// has no admin role
ctx = &AuthContext{
Roles: []string{projectAdminRole, developerRole, guestRole},
}
assert.False(t, ctx.IsSysAdmin())
// has admin role
ctx = &AuthContext{
Roles: []string{sysAdminRole},
}
assert.True(t, ctx.IsSysAdmin())
}
func TestGetProjectRoles(t *testing.T) {
ctx := &AuthContext{
Projects: []*project{
&project{
Name: "project",
Roles: []string{projectAdminRole, developerRole, guestRole},
},
},
}
// test with name
roles := ctx.GetProjectRoles("project")
assert.Equal(t, 3, len(roles))
// TODO add test case with ID
}
func TestGetMyProjects(t *testing.T) {
ctx := &AuthContext{
Projects: []*project{
&project{
Name: "project1",
Roles: []string{projectAdminRole},
},
&project{
Name: "project2",
Roles: []string{developerRole},
},
},
}
projects := ctx.GetMyProjects()
assert.Equal(t, 2, len(projects))
}

View File

@ -14,6 +14,10 @@
package security
import (
"github.com/vmware/harbor/src/common/models"
)
// Context abstracts the operations related with authN and authZ
type Context interface {
// IsAuthenticated returns whether the context has been authenticated or not
@ -28,4 +32,6 @@ type Context interface {
HasWritePerm(projectIDOrName interface{}) bool
// HasAllPerm returns whether the user has all permissions to the project
HasAllPerm(projectIDOrName interface{}) bool
GetMyProjects() ([]*models.Project, error)
GetProjectRoles(projectIDOrName interface{}) []int
}

View File

@ -16,6 +16,7 @@ package local
import (
"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"
"github.com/vmware/harbor/src/ui/projectmanager"
@ -60,18 +61,6 @@ func (s *SecurityContext) IsSysAdmin() bool {
// HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
// not exist
exist, err := s.pm.Exist(projectIDOrName)
if err != nil {
log.Errorf("failed to check the existence of project %v: %v",
projectIDOrName, err)
return false
}
if !exist {
return false
}
// public project
public, err := s.pm.IsPublic(projectIDOrName)
if err != nil {
@ -93,23 +82,9 @@ func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
return true
}
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
if err != nil {
log.Errorf("failed to get roles of user %s to project %v: %v",
s.GetUsername(), projectIDOrName, err)
return false
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin,
common.RoleDeveloper,
common.RoleGuest:
return true
}
}
return false
return len(roles) > 0
}
// HasWritePerm returns whether the user has write permission to the project
@ -118,30 +93,12 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
return false
}
// project does not exist
exist, err := s.pm.Exist(projectIDOrName)
if err != nil {
log.Errorf("failed to check the existence of project %v: %v",
projectIDOrName, err)
return false
}
if !exist {
return false
}
// system admin
if s.IsSysAdmin() {
return true
}
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
if err != nil {
log.Errorf("failed to get roles of user %s to project %v: %v",
s.GetUsername(), projectIDOrName, err)
return false
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin,
@ -159,30 +116,12 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return false
}
// project does not exist
exist, err := s.pm.Exist(projectIDOrName)
if err != nil {
log.Errorf("failed to check the existence of project %v: %v",
projectIDOrName, err)
return false
}
if !exist {
return false
}
// system admin
if s.IsSysAdmin() {
return true
}
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
if err != nil {
log.Errorf("failed to get roles of user %s to project %v: %v",
s.GetUsername(), projectIDOrName, err)
return false
}
roles := s.GetProjectRoles(projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin:
@ -192,3 +131,61 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
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 {
return []int{}
}
roles := []int{}
user, err := dao.GetUser(models.User{
Username: s.GetUsername(),
})
if err != nil {
log.Errorf("failed to get user %s: %v", s.GetUsername(), err)
return roles
}
if user == nil {
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)
return roles
}
if project == nil {
log.Errorf("project %v not found", projectIDOrName)
return roles
}
roleList, err := dao.GetUserProjectRoles(user.UserID, project.ProjectID)
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":
roles = append(roles, common.RoleProjectAdmin)
case "RWS":
roles = append(roles, common.RoleDeveloper)
case "RS":
roles = append(roles, common.RoleGuest)
}
}
return roles
}

View File

@ -15,109 +15,132 @@
package local
import (
"fmt"
"os"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"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"
"github.com/vmware/harbor/src/ui/projectmanager/db"
)
var (
public = &models.Project{
Name: "public_project",
Public: 1,
}
private = &models.Project{
Name: "private_project",
Public: 0,
Name: "private_project",
OwnerID: 1,
}
read = &models.Project{
Name: "has_read_perm_project",
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",
}
write = &models.Project{
Name: "has_write_perm_project",
}
all = &models.Project{
Name: "has_all_perm_project",
}
pm = &db.ProjectManager{}
)
type fakePM struct {
projects []*models.Project
roles map[string][]int
}
func (f *fakePM) IsPublic(projectIDOrName interface{}) (bool, error) {
for _, project := range f.projects {
if project.Name == projectIDOrName.(string) {
return project.Public == 1, nil
}
func TestMain(m *testing.M) {
dbHost := os.Getenv("MYSQL_HOST")
if len(dbHost) == 0 {
log.Fatalf("environment variable MYSQL_HOST is not set")
}
return false, nil
}
func (f *fakePM) GetRoles(username string, projectIDOrName interface{}) ([]int, error) {
return f.roles[projectIDOrName.(string)], nil
}
func (f *fakePM) Get(projectIDOrName interface{}) (*models.Project, error) {
for _, project := range f.projects {
if project.Name == projectIDOrName.(string) {
return project, nil
}
dbPortStr := os.Getenv("MYSQL_PORT")
if len(dbPortStr) == 0 {
log.Fatalf("environment variable MYSQL_PORT is not set")
}
return nil, nil
}
func (f *fakePM) Exist(projectIDOrName interface{}) (bool, error) {
for _, project := range f.projects {
if project.Name == projectIDOrName.(string) {
return true, nil
}
dbPort, err := strconv.Atoi(dbPortStr)
if err != nil {
log.Fatalf("invalid MYSQL_PORT: %v", err)
}
dbUser := os.Getenv("MYSQL_USR")
if len(dbUser) == 0 {
log.Fatalf("environment variable MYSQL_USR is not set")
}
return false, nil
}
// nil implement
func (f *fakePM) GetPublic() ([]*models.Project, error) {
return []*models.Project{}, nil
}
dbPassword := os.Getenv("MYSQL_PWD")
dbDatabase := os.Getenv("MYSQL_DATABASE")
if len(dbDatabase) == 0 {
log.Fatalf("environment variable MYSQL_DATABASE is not set")
}
// nil implement
func (f *fakePM) GetByMember(username string) ([]*models.Project, error) {
return []*models.Project{}, nil
}
database := &models.Database{
Type: "mysql",
MySQL: &models.MySQL{
Host: dbHost,
Port: dbPort,
Username: dbUser,
Password: dbPassword,
Database: dbDatabase,
},
}
// nil implement
func (f *fakePM) Create(*models.Project) (int64, error) {
return 0, fmt.Errorf("not support")
}
log.Infof("MYSQL_HOST: %s, MYSQL_USR: %s, MYSQL_PORT: %d, MYSQL_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
// nil implement
func (f *fakePM) Delete(projectIDOrName interface{}) error {
return fmt.Errorf("not support")
}
if err := dao.InitDatabase(database); err != nil {
log.Fatalf("failed to initialize database: %v", err)
}
// nil implement
func (f *fakePM) Update(projectIDOrName interface{}, project *models.Project) error {
return fmt.Errorf("not support")
}
// 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))
// nil implement
func (f *fakePM) GetAll(*models.ProjectQueryParam, ...*models.BaseProjectCollection) ([]*models.Project, error) {
return []*models.Project{}, nil
}
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))
// nil implement
func (f *fakePM) GetHasReadPerm(username ...string) ([]*models.Project, error) {
return []*models.Project{}, nil
}
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))
// nil implement
func (f *fakePM) GetTotal(*models.ProjectQueryParam, ...*models.BaseProjectCollection) (int64, error) {
return 0, nil
// 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)
// add project members
err = dao.AddProjectMember(private.ProjectID, projectAdminUser.UserID, common.RoleProjectAdmin)
if err != nil {
log.Fatalf("failed to add member: %v", err)
}
defer dao.DeleteProjectMember(private.ProjectID, projectAdminUser.UserID)
err = dao.AddProjectMember(private.ProjectID, developerUser.UserID, common.RoleDeveloper)
if err != nil {
log.Fatalf("failed to add member: %v", err)
}
defer dao.DeleteProjectMember(private.ProjectID, developerUser.UserID)
err = dao.AddProjectMember(private.ProjectID, guestUser.UserID, common.RoleGuest)
if err != nil {
log.Fatalf("failed to add member: %v", err)
}
defer dao.DeleteProjectMember(private.ProjectID, guestUser.UserID)
os.Exit(m.Run())
}
func TestIsAuthenticated(t *testing.T) {
@ -164,147 +187,104 @@ func TestIsSysAdmin(t *testing.T) {
}
func TestHasReadPerm(t *testing.T) {
pm := &fakePM{
projects: []*models.Project{public, private, read},
roles: map[string][]int{
"has_read_perm_project": []int{common.RoleGuest},
},
}
// non-exist project
ctx := NewSecurityContext(nil, pm)
assert.False(t, ctx.HasReadPerm("non_exist_project"))
// public project
ctx = NewSecurityContext(nil, pm)
assert.True(t, ctx.HasReadPerm("public_project"))
ctx := NewSecurityContext(nil, pm)
assert.True(t, ctx.HasReadPerm("library"))
// private project, unauthenticated
ctx = NewSecurityContext(nil, pm)
assert.False(t, ctx.HasReadPerm("private_project"))
assert.False(t, ctx.HasReadPerm(private.Name))
// private project, authenticated, has no perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.False(t, ctx.HasReadPerm("private_project"))
assert.False(t, ctx.HasReadPerm(private.Name))
// private project, authenticated, has read perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.True(t, ctx.HasReadPerm("has_read_perm_project"))
ctx = NewSecurityContext(guestUser, pm)
assert.True(t, ctx.HasReadPerm(private.Name))
// private project, authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
Username: "admin",
HasAdminRole: 1,
}, pm)
assert.True(t, ctx.HasReadPerm("private_project"))
// non-exist project, authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, pm)
assert.False(t, ctx.HasReadPerm("non_exist_project"))
assert.True(t, ctx.HasReadPerm(private.Name))
}
func TestHasWritePerm(t *testing.T) {
pm := &fakePM{
projects: []*models.Project{read, write, private},
roles: map[string][]int{
"has_read_perm_project": []int{common.RoleGuest},
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
},
}
// unauthenticated
ctx := NewSecurityContext(nil, pm)
assert.False(t, ctx.HasWritePerm("has_write_perm_project"))
// authenticated, non-exist project
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.False(t, ctx.HasWritePerm("non_exist_project"))
assert.False(t, ctx.HasWritePerm(private.Name))
// authenticated, has read perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.False(t, ctx.HasWritePerm("has_read_perm_project")) // authenticated, has read perm
ctx = NewSecurityContext(guestUser, pm)
assert.False(t, ctx.HasWritePerm(private.Name))
// authenticated, has write perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.True(t, ctx.HasWritePerm("has_write_perm_project"))
ctx = NewSecurityContext(developerUser, pm)
assert.True(t, ctx.HasWritePerm(private.Name))
// authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
Username: "admin",
HasAdminRole: 1,
}, pm)
assert.True(t, ctx.HasReadPerm("private_project"))
// authenticated, system admin, non-exist project
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, pm)
assert.False(t, ctx.HasReadPerm("non_exist_project"))
assert.True(t, ctx.HasReadPerm(private.Name))
}
func TestHasAllPerm(t *testing.T) {
pm := &fakePM{
projects: []*models.Project{read, write, all, private},
roles: map[string][]int{
"has_read_perm_project": []int{common.RoleGuest},
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
"has_all_perm_project": []int{common.RoleGuest, common.RoleDeveloper, common.RoleProjectAdmin},
},
}
// unauthenticated
ctx := NewSecurityContext(nil, pm)
assert.False(t, ctx.HasAllPerm("has_all_perm_project"))
// authenticated, non-exist project
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.False(t, ctx.HasAllPerm("non_exist_project"))
// authenticated, has read perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.False(t, ctx.HasAllPerm("has_read_perm_project"))
// authenticated, has write perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.False(t, ctx.HasAllPerm("has_write_perm_project"))
assert.False(t, ctx.HasAllPerm(private.Name))
// authenticated, has all perms
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pm)
assert.True(t, ctx.HasAllPerm("has_all_perm_project"))
ctx = NewSecurityContext(projectAdminUser, pm)
assert.True(t, ctx.HasAllPerm(private.Name))
// authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
Username: "admin",
HasAdminRole: 1,
}, pm)
assert.True(t, ctx.HasAllPerm("private_project"))
// authenticated, system admin, non-exist project
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, pm)
assert.False(t, ctx.HasAllPerm("non_exist_project"))
assert.True(t, ctx.HasAllPerm(private.Name))
}
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])
}

View File

@ -15,6 +15,10 @@
package secret
import (
"fmt"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/secret"
"github.com/vmware/harbor/src/common/utils/log"
)
@ -79,3 +83,17 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return false
}
// GetMyProjects ...
func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
return nil, fmt.Errorf("GetMyProjects is unsupported")
}
// GetProjectRoles return guest role if has read permission, otherwise return nil
func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
roles := []int{}
if s.HasReadPerm(projectIDOrName) {
roles = append(roles, common.RoleGuest)
}
return roles
}

View File

@ -18,6 +18,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/secret"
)
@ -132,3 +133,34 @@ func TestHasAllPerm(t *testing.T) {
hasAllPerm = context.HasAllPerm(1)
assert.False(t, hasAllPerm)
}
func TestGetMyProjects(t *testing.T) {
context := NewSecurityContext("secret",
secret.NewStore(map[string]string{
"secret": "username",
}))
_, err := context.GetMyProjects()
assert.NotNil(t, err)
}
func TestGetProjectRoles(t *testing.T) {
//invalid secret
context := NewSecurityContext("invalid_secret",
secret.NewStore(map[string]string{
"jobservice_secret": secret.JobserviceUser,
}))
roles := context.GetProjectRoles("any_project")
assert.Equal(t, 0, len(roles))
// valid secret
context = NewSecurityContext("jobservice_secret",
secret.NewStore(map[string]string{
"jobservice_secret": secret.JobserviceUser,
}))
roles = context.GetProjectRoles("any_project")
assert.Equal(t, 1, len(roles))
assert.Equal(t, common.RoleGuest, roles[0])
}

View File

@ -75,7 +75,7 @@ func (l *LogAPI) Get() {
}
if !l.isSysAdmin {
projects, err := l.ProjectMgr.GetByMember(l.username)
projects, err := l.SecurityCtx.GetMyProjects()
if err != nil {
l.HandleInternalServerError(fmt.Sprintf(
"failed to get projects of user %s: %v", l.username, err))

View File

@ -311,13 +311,7 @@ func (p *ProjectAPI) List() {
for _, project := range projects {
if p.SecurityCtx.IsAuthenticated() {
roles, err := p.ProjectMgr.GetRoles(p.SecurityCtx.GetUsername(), project.ProjectID)
if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get roles of user %s to project %d: %v",
p.SecurityCtx.GetUsername(), project.ProjectID, err))
return
}
roles := p.SecurityCtx.GetProjectRoles(project.ProjectID)
if len(roles) != 0 {
project.Role = roles[0]
}

View File

@ -608,7 +608,7 @@ func (ra *RepositoryAPI) GetTopRepos() {
return
}
if ra.SecurityCtx.IsAuthenticated() {
list, err := ra.ProjectMgr.GetByMember(ra.SecurityCtx.GetUsername())
list, err := ra.SecurityCtx.GetMyProjects()
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get projects which the user %s is a member of: %v",
ra.SecurityCtx.GetUsername(), err))

View File

@ -43,23 +43,43 @@ type searchResult struct {
func (s *SearchAPI) Get() {
keyword := s.GetString("q")
isAuthenticated := s.SecurityCtx.IsAuthenticated()
username := s.SecurityCtx.GetUsername()
isSysAdmin := s.SecurityCtx.IsSysAdmin()
var projects []*models.Project
var err error
if !isAuthenticated {
projects, err = s.ProjectMgr.GetPublic()
} else if isSysAdmin {
if isSysAdmin {
projects, err = s.ProjectMgr.GetAll(nil)
if err != nil {
s.HandleInternalServerError(fmt.Sprintf(
"failed to get projects: %v", err))
return
}
} else {
projects, err = s.ProjectMgr.GetHasReadPerm(username)
}
if err != nil {
s.HandleInternalServerError(fmt.Sprintf(
"failed to get projects: %v", err))
return
projects, err = s.ProjectMgr.GetPublic()
if err != nil {
s.HandleInternalServerError(fmt.Sprintf(
"failed to get projects: %v", err))
return
}
if isAuthenticated {
mys, err := s.SecurityCtx.GetMyProjects()
if err != nil {
s.HandleInternalServerError(fmt.Sprintf(
"failed to get projects: %v", err))
return
}
exist := map[int64]bool{}
for _, p := range projects {
exist[p.ProjectID] = true
}
for _, p := range mys {
if !exist[p.ProjectID] {
projects = append(projects, p)
}
}
}
}
projectSorter := &models.ProjectSorter{Projects: projects}
@ -71,13 +91,7 @@ func (s *SearchAPI) Get() {
}
if isAuthenticated {
roles, err := s.ProjectMgr.GetRoles(username, p.ProjectID)
if err != nil {
s.HandleInternalServerError(fmt.Sprintf("failed to get roles of user %s to project %d: %v",
username, p.ProjectID, err))
return
}
roles := s.SecurityCtx.GetProjectRoles(p.ProjectID)
if len(roles) != 0 {
p.Role = roles[0]
}

View File

@ -18,7 +18,6 @@ import (
"fmt"
"time"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
)
@ -62,48 +61,6 @@ func (p *ProjectManager) IsPublic(projectIDOrName interface{}) (bool, error) {
return project.Public == 1, nil
}
// GetRoles return a role list which contains the user's roles to the project
func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) ([]int, error) {
roles := []int{}
user, err := dao.GetUser(models.User{
Username: username,
})
if err != nil {
return nil, fmt.Errorf("failed to get user %s: %v",
username, err)
}
if user == nil {
return roles, nil
}
project, err := p.Get(projectIDOrName)
if err != nil {
return nil, err
}
if project == nil {
return roles, nil
}
roleList, err := dao.GetUserProjectRoles(user.UserID, project.ProjectID)
if err != nil {
return nil, err
}
for _, role := range roleList {
switch role.RoleCode {
case "MDRWS":
roles = append(roles, common.RoleProjectAdmin)
case "RWS":
roles = append(roles, common.RoleDeveloper)
case "RS":
roles = append(roles, common.RoleGuest)
}
}
return roles, nil
}
// GetPublic returns all public projects
func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
t := true
@ -112,20 +69,6 @@ func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
})
}
// GetByMember returns all projects which the user is a member of
func (p *ProjectManager) GetByMember(username string) (
[]*models.Project, error) {
if len(username) == 0 {
return []*models.Project{}, nil
}
return p.GetAll(&models.ProjectQueryParam{
Member: &models.MemberQuery{
Name: username,
},
})
}
// Create ...
func (p *ProjectManager) Create(project *models.Project) (int64, error) {
if project == nil {
@ -204,13 +147,3 @@ func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam, base ...*mode
int64, error) {
return dao.GetTotalOfProjects(query, base...)
}
// GetHasReadPerm returns projects which are public or the user is a member of
func (p *ProjectManager) GetHasReadPerm(username ...string) (
[]*models.Project, error) {
if len(username) == 0 || len(username[0]) == 0 {
return p.GetPublic()
}
return dao.GetHasReadPermProjects(username[0])
}

View File

@ -20,7 +20,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"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"
@ -121,25 +120,6 @@ func TestIsPublic(t *testing.T) {
assert.False(t, public)
}
func TestGetRoles(t *testing.T) {
pm := &ProjectManager{}
// non exist user
roles, err := pm.GetRoles("non_exist_user", int64(1))
assert.Nil(t, err)
assert.Equal(t, []int{}, roles)
// exist project
roles, err = pm.GetRoles("admin", "library")
assert.Nil(t, err)
assert.Equal(t, []int{common.RoleProjectAdmin}, roles)
// non-exist project
roles, err = pm.GetRoles("admin", "non_exist_project")
assert.Nil(t, err)
assert.Equal(t, []int{}, roles)
}
func TestGetPublic(t *testing.T) {
pm := &ProjectManager{}
projects, err := pm.GetPublic()
@ -151,19 +131,6 @@ func TestGetPublic(t *testing.T) {
}
}
func TestGetByMember(t *testing.T) {
pm := &ProjectManager{}
// empty username
projects, err := pm.GetByMember("")
assert.Nil(t, err)
assert.Equal(t, 0, len(projects))
//non-empty username
projects, err = pm.GetByMember("admin")
assert.Nil(t, err)
assert.NotEqual(t, 0, len(projects))
}
func TestCreateAndDelete(t *testing.T) {
pm := &ProjectManager{}
@ -304,58 +271,3 @@ func TestGetAll(t *testing.T) {
}
assert.True(t, exist)
}
func TestGetHasReadPerm(t *testing.T) {
pm := &ProjectManager{}
// do not pass username
projects, err := pm.GetHasReadPerm()
assert.Nil(t, err)
assert.NotEqual(t, 0, len(projects))
exist := false
for _, project := range projects {
if project.ProjectID == 1 {
exist = true
break
}
}
assert.True(t, exist)
// username is nil
projects, err = pm.GetHasReadPerm("")
assert.Nil(t, err)
assert.NotEqual(t, 0, len(projects))
exist = false
for _, project := range projects {
if project.ProjectID == 1 {
exist = true
break
}
}
assert.True(t, exist)
// valid username
id, err := pm.Create(&models.Project{
Name: "get_has_read_perm_test",
OwnerID: 1,
Public: 0,
})
assert.Nil(t, err)
defer pm.Delete(id)
projects, err = pm.GetHasReadPerm("admin")
assert.Nil(t, err)
assert.NotEqual(t, 0, len(projects))
exist1 := false
exist2 := false
for _, project := range projects {
if project.ProjectID == 1 {
exist1 = true
}
if project.ProjectID == id {
exist2 = true
}
}
assert.True(t, exist1)
assert.True(t, exist2)
}

View File

@ -24,11 +24,8 @@ type ProjectManager interface {
Get(projectIDOrName interface{}) (*models.Project, error)
IsPublic(projectIDOrName interface{}) (bool, error)
Exist(projectIDOrName interface{}) (bool, error)
GetRoles(username string, projectIDOrName interface{}) ([]int, error)
// get all public project
GetPublic() ([]*models.Project, error)
// get projects which the user is a member of
GetByMember(username string) ([]*models.Project, error)
Create(*models.Project) (int64, error)
Delete(projectIDOrName interface{}) error
Update(projectIDOrName interface{}, project *models.Project) error
@ -36,8 +33,4 @@ type ProjectManager interface {
GetAll(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) ([]*models.Project, error)
// GetTotal returns the total count according to the query parameters
GetTotal(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (int64, error)
// GetHasReadPerm returns a project list which the user has read
// permission of. The list should contains all public projects and
// projects which the user is a member of if the username is not nil
GetHasReadPerm(username ...string) ([]*models.Project, error)
}

View File

@ -25,9 +25,7 @@ import (
"strconv"
"strings"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/security/authcontext"
er "github.com/vmware/harbor/src/common/utils/error"
"github.com/vmware/harbor/src/common/utils/log"
)
@ -227,6 +225,7 @@ func (p *ProjectManager) Exist(projectIDOrName interface{}) (bool, error) {
return project != nil, nil
}
/*
// GetRoles gets roles that the user has to the project
// This method is used in GET /projects API.
// Jobservice calls GET /projects API to get information of source
@ -279,6 +278,7 @@ func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{})
return roles, nil
}
*/
func (p *ProjectManager) getIDbyHarborIDOrName(projectIDOrName interface{}) (string, error) {
pro, err := p.get(projectIDOrName)
@ -301,26 +301,6 @@ func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
})
}
// GetByMember ...
func (p *ProjectManager) GetByMember(username string) ([]*models.Project, error) {
projects := []*models.Project{}
ctx, err := authcontext.GetAuthCtxOfUser(p.client, p.endpoint, p.getToken(), username)
if err != nil {
return projects, err
}
names := ctx.GetMyProjects()
for _, name := range names {
project, err := p.Get(name)
if err != nil {
return projects, err
}
projects = append(projects, project)
}
return projects, nil
}
// Create ...
func (p *ProjectManager) Create(pro *models.Project) (int64, error) {
proj := &project{
@ -407,11 +387,6 @@ func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam, base ...*mode
return int64(len(projects)), err
}
// GetHasReadPerm ...
func (p *ProjectManager) GetHasReadPerm(username ...string) ([]*models.Project, error) {
return nil, errors.New("GetHasReadPerm is unsupported")
}
func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, error) {
req, err := http.NewRequest(method, p.endpoint+path, body)
if err != nil {

View File

@ -292,33 +292,6 @@ func TestExist(t *testing.T) {
assert.True(t, exist)
}
func TestGetRoles(t *testing.T) {
pm := NewProjectManager(client, endpoint, tokenReader)
// nil username, nil project
roles, err := pm.GetRoles("", nil)
assert.Nil(t, err)
assert.Zero(t, len(roles))
// non-exist project
_, err = pm.GetRoles("user01", "non_exist_project")
assert.NotNil(t, err)
// exist project
name := "project_for_test_get_roles"
id, err := pm.Create(&models.Project{
Name: name,
})
require.Nil(t, err)
defer delete(t, id)
roles, err = pm.GetRoles("user01", id)
assert.Nil(t, err)
assert.Zero(t, len(roles))
// TODO add test cases for real role of user
}
func TestGetPublic(t *testing.T) {
pm := NewProjectManager(client, endpoint, tokenReader)
@ -348,11 +321,6 @@ func TestGetPublic(t *testing.T) {
assert.True(t, found)
}
// TODO add test case
func TestGetByMember(t *testing.T) {
}
func TestCreate(t *testing.T) {
pm := NewProjectManager(client, endpoint, tokenReader)
@ -492,12 +460,6 @@ func TestGetTotal(t *testing.T) {
assert.Equal(t, total1+1, total2)
}
func TestGetHasReadPerm(t *testing.T) {
pm := NewProjectManager(client, endpoint, tokenReader)
_, err := pm.GetHasReadPerm()
assert.NotNil(t, err)
}
func delete(t *testing.T, id int64) {
pm := NewProjectManager(client, endpoint, tokenReader)
if err := pm.Delete(id); err != nil {

View File

@ -29,6 +29,7 @@ import (
"runtime"
"testing"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/test"
"github.com/vmware/harbor/src/ui/config"
)
@ -225,6 +226,12 @@ func (f *fakeSecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
func (f *fakeSecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return false
}
func (f *fakeSecurityContext) GetMyProjects() ([]*models.Project, error) {
return nil, nil
}
func (f *fakeSecurityContext) GetProjectRoles(interface{}) []int {
return nil
}
func TestFilterAccess(t *testing.T) {
//TODO put initial data in DB to verify repository filter.