This commit is contained in:
Wenkai Yin 2017-05-17 17:15:43 +08:00
parent e1c1b8ec34
commit b5279ea1f1
7 changed files with 445 additions and 261 deletions

View File

@ -17,6 +17,7 @@ package rbac
import ( import (
"github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/projectmanager" "github.com/vmware/harbor/src/ui/projectmanager"
) )
@ -60,12 +61,25 @@ func (s *SecurityContext) IsSysAdmin() bool {
// HasReadPerm returns whether the user has read permission to the project // HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool { func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
// not exist // not exist
if !s.pm.Exist(projectIDOrName) { 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 return false
} }
// public project // public project
if s.pm.IsPublic(projectIDOrName) { public, err := s.pm.IsPublic(projectIDOrName)
if err != nil {
log.Errorf("failed to check the public of project %v: %v",
projectIDOrName, err)
return false
}
if public {
return true return true
} }
@ -79,7 +93,13 @@ func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
return true return true
} }
roles := s.pm.GetRoles(s.GetUsername(), 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 { for _, role := range roles {
switch role { switch role {
case common.RoleProjectAdmin, case common.RoleProjectAdmin,
@ -99,7 +119,14 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
} }
// project does not exist // project does not exist
if !s.pm.Exist(projectIDOrName) { 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 return false
} }
@ -108,7 +135,13 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
return true return true
} }
roles := s.pm.GetRoles(s.GetUsername(), 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 { for _, role := range roles {
switch role { switch role {
case common.RoleProjectAdmin, case common.RoleProjectAdmin,
@ -127,7 +160,14 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
} }
// project does not exist // project does not exist
if !s.pm.Exist(projectIDOrName) { 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 return false
} }
@ -136,7 +176,13 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return true return true
} }
roles := s.pm.GetRoles(s.GetUsername(), 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 { for _, role := range roles {
switch role { switch role {
case common.RoleProjectAdmin: case common.RoleProjectAdmin:

View File

@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/ui/projectmanager"
) )
var ( var (
@ -52,42 +53,42 @@ type fakePM struct {
roles map[string][]int roles map[string][]int
} }
func (f *fakePM) IsPublic(projectIDOrName interface{}) bool { func (f *fakePM) IsPublic(projectIDOrName interface{}) (bool, error) {
for _, project := range f.projects { for _, project := range f.projects {
if project.Name == projectIDOrName.(string) { if project.Name == projectIDOrName.(string) {
return project.Public == 1 return project.Public == 1, nil
} }
} }
return false return false, nil
} }
func (f *fakePM) GetRoles(username string, projectIDOrName interface{}) []int { func (f *fakePM) GetRoles(username string, projectIDOrName interface{}) ([]int, error) {
return f.roles[projectIDOrName.(string)] return f.roles[projectIDOrName.(string)], nil
} }
func (f *fakePM) Get(projectIDOrName interface{}) *models.Project { func (f *fakePM) Get(projectIDOrName interface{}) (*models.Project, error) {
for _, project := range f.projects { for _, project := range f.projects {
if project.Name == projectIDOrName.(string) { if project.Name == projectIDOrName.(string) {
return project return project, nil
} }
} }
return nil return nil, nil
} }
func (f *fakePM) Exist(projectIDOrName interface{}) bool { func (f *fakePM) Exist(projectIDOrName interface{}) (bool, error) {
for _, project := range f.projects { for _, project := range f.projects {
if project.Name == projectIDOrName.(string) { if project.Name == projectIDOrName.(string) {
return true return true, nil
} }
} }
return false return false, nil
} }
// nil implement // nil implement
func (f *fakePM) GetPublic() []*models.Project { func (f *fakePM) GetPublic() ([]*models.Project, error) {
return []*models.Project{} return []*models.Project{}, nil
} }
// nil implement // nil implement
func (f *fakePM) GetByMember(username string) []*models.Project { func (f *fakePM) GetByMember(username string) ([]*models.Project, error) {
return []*models.Project{} return []*models.Project{}, nil
} }
// nil implement // nil implement
@ -106,9 +107,13 @@ func (f *fakePM) Update(projectIDOrName interface{}, project *models.Project) er
} }
// nil implement // nil implement
func (f *fakePM) GetAll(owner, name, public, member string, role int, page, func (f *fakePM) GetAll(*projectmanager.QueryParam) ([]*models.Project, error) {
size int64) ([]*models.Project, int64) { return []*models.Project{}, nil
return []*models.Project{}, 0 }
// nil implement
func (f *fakePM) GetTotal(*projectmanager.QueryParam) (int64, error) {
return 0, nil
} }
func TestIsAuthenticated(t *testing.T) { func TestIsAuthenticated(t *testing.T) {

View File

@ -24,6 +24,7 @@ import (
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config" "github.com/vmware/harbor/src/ui/config"
"github.com/vmware/harbor/src/ui/projectmanager"
"strconv" "strconv"
"time" "time"
@ -32,6 +33,7 @@ import (
// ProjectAPI handles request to /api/projects/{} /api/projects/{}/logs // ProjectAPI handles request to /api/projects/{} /api/projects/{}/logs
type ProjectAPI struct { type ProjectAPI struct {
BaseController BaseController
project *models.Project
} }
type projectReq struct { type projectReq struct {
@ -44,6 +46,38 @@ const projectNameMinLen int = 2
const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*` const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*`
const dupProjectPattern = `Duplicate entry '\w+' for key 'name'` const dupProjectPattern = `Duplicate entry '\w+' for key 'name'`
// Prepare validates the URL and the user
func (p *ProjectAPI) Prepare() {
p.BaseController.Prepare()
if len(p.GetStringFromPath(":id")) != 0 {
id, err := p.GetInt64FromPath(":id")
if err != nil || id <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", id)
}
p.HandleBadRequest(text)
return
}
project, err := p.ProjectMgr.Get(id)
if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get project %d: %v",
id, err))
return
}
if project == nil {
p.HandleNotFound(fmt.Sprintf("project %d not found", id))
return
}
p.project = project
}
}
// Post ... // Post ...
func (p *ProjectAPI) Post() { func (p *ProjectAPI) Post() {
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
@ -70,7 +104,13 @@ func (p *ProjectAPI) Post() {
return return
} }
if p.ProjectMgr.Exist(pro.ProjectName) { exist, err := p.ProjectMgr.Exist(pro.ProjectName)
if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
pro.ProjectName, err))
return
}
if exist {
p.RenderError(http.StatusConflict, "") p.RenderError(http.StatusConflict, "")
return return
} }
@ -117,7 +157,13 @@ func (p *ProjectAPI) Head() {
return return
} }
project := p.ProjectMgr.Get(name) project, err := p.ProjectMgr.Get(name)
if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get project %s: %v",
name, err))
return
}
if project == nil { if project == nil {
p.HandleNotFound(fmt.Sprintf("project %s not found", name)) p.HandleNotFound(fmt.Sprintf("project %s not found", name))
return return
@ -126,99 +172,63 @@ func (p *ProjectAPI) Head() {
// Get ... // Get ...
func (p *ProjectAPI) Get() { func (p *ProjectAPI) Get() {
id, err := p.GetInt64FromPath(":id") if p.project.Public == 0 {
if err != nil || id <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", id)
}
p.HandleBadRequest(text)
return
}
project := p.ProjectMgr.Get(id)
if project == nil {
p.HandleNotFound(fmt.Sprintf("project %d not found", id))
return
}
if project.Public == 0 {
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.HandleUnauthorized()
return return
} }
if !p.SecurityCtx.HasReadPerm(id) { if !p.SecurityCtx.HasReadPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername()) p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
} }
p.Data["json"] = project p.Data["json"] = p.project
p.ServeJSON() p.ServeJSON()
} }
// Delete ... // Delete ...
func (p *ProjectAPI) Delete() { func (p *ProjectAPI) Delete() {
id, err := p.GetInt64FromPath(":id")
if err != nil || id <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", id)
}
p.HandleBadRequest(text)
return
}
project := p.ProjectMgr.Get(id)
if project == nil {
p.HandleNotFound(fmt.Sprintf("project %d not found", id))
return
}
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.HandleUnauthorized()
return return
} }
if !p.SecurityCtx.HasAllPerm(id) { if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername()) p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
contains, err := projectContainsRepo(project.Name) contains, err := projectContainsRepo(p.project.Name)
if err != nil { if err != nil {
log.Errorf("failed to check whether project %s contains any repository: %v", project.Name, err) log.Errorf("failed to check whether project %s contains any repository: %v", p.project.Name, err)
p.CustomAbort(http.StatusInternalServerError, "") p.CustomAbort(http.StatusInternalServerError, "")
} }
if contains { if contains {
p.CustomAbort(http.StatusPreconditionFailed, "project contains repositores, can not be deleted") p.CustomAbort(http.StatusPreconditionFailed, "project contains repositores, can not be deleted")
} }
contains, err = projectContainsPolicy(id) contains, err = projectContainsPolicy(p.project.ProjectID)
if err != nil { if err != nil {
log.Errorf("failed to check whether project %s contains any policy: %v", project.Name, err) log.Errorf("failed to check whether project %s contains any policy: %v", p.project.Name, err)
p.CustomAbort(http.StatusInternalServerError, "") p.CustomAbort(http.StatusInternalServerError, "")
} }
if contains { if contains {
p.CustomAbort(http.StatusPreconditionFailed, "project contains policies, can not be deleted") p.CustomAbort(http.StatusPreconditionFailed, "project contains policies, can not be deleted")
} }
if err = p.ProjectMgr.Delete(id); err != nil { if err = p.ProjectMgr.Delete(p.project.ProjectID); err != nil {
p.HandleInternalServerError( p.HandleInternalServerError(
fmt.Sprintf("failed to delete project %d: %v", id, err)) fmt.Sprintf("failed to delete project %d: %v", p.project.ProjectID, err))
return return
} }
go func() { go func() {
if err := dao.AddAccessLog(models.AccessLog{ if err := dao.AddAccessLog(models.AccessLog{
Username: p.SecurityCtx.GetUsername(), Username: p.SecurityCtx.GetUsername(),
ProjectID: id, ProjectID: p.project.ProjectID,
RepoName: project.Name + "/", RepoName: p.project.Name + "/",
RepoTag: "N/A", RepoTag: "N/A",
Operation: "delete", Operation: "delete",
OpTime: time.Now(), OpTime: time.Now(),
@ -250,53 +260,61 @@ func projectContainsPolicy(id int64) (bool, error) {
// TODO refacter pattern to: // TODO refacter pattern to:
// /api/repositories?owner=xxx&name=xxx&public=true&member=xxx&role=1&page=1&size=3 // /api/repositories?owner=xxx&name=xxx&public=true&member=xxx&role=1&page=1&size=3
func (p *ProjectAPI) List() { func (p *ProjectAPI) List() {
query := &projectmanager.QueryParam{}
// query conditions: query.Name = p.GetString("project_name")
var ( public := p.GetString("is_public")
owner string // the username of project owner
name string // the project name
public string // the project is public or not
member string // the username of the member
role int // role of the member specified by member parameter
page int64 // pagination
size int64 // pagination
)
name = p.GetString("project_name")
public = p.GetString("is_public")
if len(public) != 0 { if len(public) != 0 {
if public != "0" && public != "1" { if public != "0" && public != "1" {
p.HandleBadRequest("is_public should be 0 or 1") p.HandleBadRequest("is_public should be 0 or 1")
return return
} }
if public == "1" { if public == "1" {
public = "true" query.Public = "true"
} }
} }
page, size = p.GetPaginationParams() if query.Public != "true" {
if public != "true" {
//if the request is not for public projects, user must login or provide credential //if the request is not for public projects, user must login or provide credential
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.HandleUnauthorized()
return return
} }
public = "" if !p.SecurityCtx.IsSysAdmin() {
member = p.SecurityCtx.GetUsername() query.Member = &projectmanager.Member{
Name: p.SecurityCtx.GetUsername(),
if p.SecurityCtx.IsSysAdmin() { }
member = ""
} }
} }
projects, total := p.ProjectMgr.GetAll(owner, name, public, member, total, err := p.ProjectMgr.GetTotal(query)
role, page, size) if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get total of projects: %v", err))
return
}
page, size := p.GetPaginationParams()
query.Pagination = &projectmanager.Pagination{
Page: page,
Size: size,
}
projects, err := p.ProjectMgr.GetAll(query)
if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get projects: %v", err))
return
}
for _, project := range projects { for _, project := range projects {
if public != "true" { if query.Public != "true" {
roles := p.ProjectMgr.GetRoles(p.SecurityCtx.GetUsername(), project.ProjectID) 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
}
if len(roles) != 0 { if len(roles) != 0 {
project.Role = roles[0] project.Role = roles[0]
} }
@ -323,30 +341,12 @@ func (p *ProjectAPI) List() {
// ToggleProjectPublic ... // ToggleProjectPublic ...
func (p *ProjectAPI) ToggleProjectPublic() { func (p *ProjectAPI) ToggleProjectPublic() {
id, err := p.GetInt64FromPath(":id")
if err != nil || id <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", id)
}
p.HandleBadRequest(text)
return
}
project := p.ProjectMgr.Get(id)
if project == nil {
p.HandleNotFound(fmt.Sprintf("project %d not found", id))
return
}
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.HandleUnauthorized()
return return
} }
if !p.SecurityCtx.HasAllPerm(id) { if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername()) p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
@ -358,40 +358,24 @@ func (p *ProjectAPI) ToggleProjectPublic() {
return return
} }
if err := p.ProjectMgr.Update(id, &models.Project{ if err := p.ProjectMgr.Update(p.project.ProjectID,
Public: req.Public, &models.Project{
}); err != nil { Public: req.Public,
p.HandleInternalServerError(fmt.Sprintf("failed to update project %d: %v", id, err)) }); err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to update project %d: %v",
p.project.ProjectID, err))
return return
} }
} }
// FilterAccessLog handles GET to /api/projects/{}/logs // FilterAccessLog handles GET to /api/projects/{}/logs
func (p *ProjectAPI) FilterAccessLog() { func (p *ProjectAPI) FilterAccessLog() {
id, err := p.GetInt64FromPath(":id")
if err != nil || id <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", id)
}
p.HandleBadRequest(text)
return
}
project := p.ProjectMgr.Get(id)
if project == nil {
p.HandleNotFound(fmt.Sprintf("project %d not found", id))
return
}
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.HandleUnauthorized()
return return
} }
if !p.SecurityCtx.HasReadPerm(id) { if !p.SecurityCtx.HasReadPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername()) p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
@ -399,7 +383,7 @@ func (p *ProjectAPI) FilterAccessLog() {
var query models.AccessLog var query models.AccessLog
p.DecodeJSONReq(&query) p.DecodeJSONReq(&query)
query.ProjectID = id query.ProjectID = p.project.ProjectID
query.BeginTime = time.Unix(query.BeginTimestamp, 0) query.BeginTime = time.Unix(query.BeginTimestamp, 0)
query.EndTime = time.Unix(query.EndTimestamp, 0) query.EndTime = time.Unix(query.EndTimestamp, 0)

View File

@ -69,7 +69,14 @@ func (ra *RepositoryAPI) Get() {
return return
} }
if !ra.ProjectMgr.Exist(projectID) { exist, err := ra.ProjectMgr.Exist(projectID)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %d: %v",
projectID, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("project %d not found", projectID)) ra.HandleNotFound(fmt.Sprintf("project %d not found", projectID))
return return
} }
@ -146,7 +153,14 @@ func (ra *RepositoryAPI) Delete() {
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
projectName, _ := utils.ParseRepository(repoName) projectName, _ := utils.ParseRepository(repoName)
if !ra.ProjectMgr.Exist(projectName) { exist, err := ra.ProjectMgr.Exist(projectName)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
projectName, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName))
return return
} }
@ -236,7 +250,18 @@ func (ra *RepositoryAPI) Delete() {
go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete)
go func(tag string) { go func(tag string) {
project := ra.ProjectMgr.Get(projectName) project, err := ra.ProjectMgr.Get(projectName)
if err != nil {
log.Errorf("failed to get the project %s: %v",
projectName, err)
return
}
if project == nil {
log.Error("project %s not found", projectName)
return
}
if err := dao.AddAccessLog(models.AccessLog{ if err := dao.AddAccessLog(models.AccessLog{
Username: ra.SecurityCtx.GetUsername(), Username: ra.SecurityCtx.GetUsername(),
ProjectID: project.ProjectID, ProjectID: project.ProjectID,
@ -250,7 +275,7 @@ func (ra *RepositoryAPI) Delete() {
}(t) }(t)
} }
exist, err := repositoryExist(repoName, rc) exist, err = repositoryExist(repoName, rc)
if err != nil { if err != nil {
log.Errorf("failed to check the existence of repository %s: %v", repoName, err) log.Errorf("failed to check the existence of repository %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "") ra.CustomAbort(http.StatusInternalServerError, "")
@ -268,7 +293,14 @@ func (ra *RepositoryAPI) GetTags() {
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
projectName, _ := utils.ParseRepository(repoName) projectName, _ := utils.ParseRepository(repoName)
if !ra.ProjectMgr.Exist(projectName) { exist, err := ra.ProjectMgr.Exist(projectName)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
projectName, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName))
return return
} }
@ -360,7 +392,14 @@ func (ra *RepositoryAPI) GetManifests() {
} }
projectName, _ := utils.ParseRepository(repoName) projectName, _ := utils.ParseRepository(repoName)
if !ra.ProjectMgr.Exist(projectName) { exist, err := ra.ProjectMgr.Exist(projectName)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
projectName, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName))
return return
} }
@ -455,10 +494,19 @@ func (ra *RepositoryAPI) GetTopRepos() {
} }
projectIDs := []int64{} projectIDs := []int64{}
projects := ra.ProjectMgr.GetPublic() projects, err := ra.ProjectMgr.GetPublic()
if err != nil {
log.Errorf("failed to get the public projects: %v", err)
return
}
if ra.SecurityCtx.IsAuthenticated() { if ra.SecurityCtx.IsAuthenticated() {
projects = append(projects, ra.ProjectMgr.GetByMember( list, err := ra.ProjectMgr.GetByMember(ra.SecurityCtx.GetUsername())
ra.SecurityCtx.GetUsername())...) if err != nil {
log.Errorf("failed to get projects which the user %s is a member of: %v",
ra.SecurityCtx.GetUsername(), err)
return
}
projects = append(projects, list...)
} }
for _, project := range projects { for _, project := range projects {

View File

@ -21,77 +21,74 @@ import (
"github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/ui/projectmanager"
) )
// ProjectManager implements pm.PM interface based on database // ProjectManager implements pm.PM interface based on database
type ProjectManager struct{} type ProjectManager struct{}
// Get ... // Get ...
func (p *ProjectManager) Get(projectIDOrName interface{}) *models.Project { func (p *ProjectManager) Get(projectIDOrName interface{}) (
*models.Project, error) {
switch projectIDOrName.(type) { switch projectIDOrName.(type) {
case string: case string:
name := projectIDOrName.(string) return dao.GetProjectByName(projectIDOrName.(string))
project, err := dao.GetProjectByName(name)
if err != nil {
log.Errorf("failed to get project %s: %v", name, err)
return nil
}
return project
case int64: case int64:
id := projectIDOrName.(int64) return dao.GetProjectByID(projectIDOrName.(int64))
project, err := dao.GetProjectByID(id)
if err != nil {
log.Errorf("failed to get project %d: %v", id, err)
return nil
}
return project
default: default:
log.Errorf("unsupported type of %v, must be string or int64", projectIDOrName) return nil, fmt.Errorf("unsupported type of %v, must be string or int64", projectIDOrName)
return nil
} }
} }
// Exist ... // Exist ...
func (p *ProjectManager) Exist(projectIDOrName interface{}) bool { func (p *ProjectManager) Exist(projectIDOrName interface{}) (bool, error) {
return p.Get(projectIDOrName) != nil project, err := p.Get(projectIDOrName)
if err != nil {
return false, err
}
return project != nil, nil
} }
// IsPublic returns whether the project is public or not // IsPublic returns whether the project is public or not
func (p *ProjectManager) IsPublic(projectIDOrName interface{}) bool { func (p *ProjectManager) IsPublic(projectIDOrName interface{}) (bool, error) {
project := p.Get(projectIDOrName) project, err := p.Get(projectIDOrName)
if project == nil { if err != nil {
return false return false, err
} }
return project.Public == 1 if project == nil {
return false, nil
}
return project.Public == 1, nil
} }
// GetRoles return a role list which contains the user's roles to the project // GetRoles return a role list which contains the user's roles to the project
func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) []int { func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) ([]int, error) {
roles := []int{} roles := []int{}
user, err := dao.GetUser(models.User{ user, err := dao.GetUser(models.User{
Username: username, Username: username,
}) })
if err != nil { if err != nil {
log.Errorf("failed to get user %s: %v", username, err) return nil, fmt.Errorf("failed to get user %s: %v",
return roles username, err)
} }
if user == nil { if user == nil {
return roles return roles, nil
} }
project := p.Get(projectIDOrName) project, err := p.Get(projectIDOrName)
if err != nil {
return nil, err
}
if project == nil { if project == nil {
return roles return roles, nil
} }
roleList, err := dao.GetUserProjectRoles(user.UserID, project.ProjectID) roleList, err := dao.GetUserProjectRoles(user.UserID, project.ProjectID)
if err != nil { if err != nil {
log.Errorf("failed to get roles for user %d to project %d: %v", return nil, err
user.UserID, project.ProjectID, err)
return roles
} }
for _, role := range roleList { for _, role := range roleList {
@ -105,17 +102,24 @@ func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{})
} }
} }
return roles return roles, nil
} }
// GetPublic returns all public projects // GetPublic returns all public projects
func (p *ProjectManager) GetPublic() []*models.Project { func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
return filter("", "", "true", "", 0, 0, 0) return p.GetAll(&projectmanager.QueryParam{
Public: "true",
})
} }
// GetByMember returns all projects which the user is a member of // GetByMember returns all projects which the user is a member of
func (p *ProjectManager) GetByMember(username string) []*models.Project { func (p *ProjectManager) GetByMember(username string) (
return filter("", "", "", username, 0, 0, 0) []*models.Project, error) {
return p.GetAll(&projectmanager.QueryParam{
Member: &projectmanager.Member{
Name: username,
},
})
} }
// Create ... // Create ...
@ -161,9 +165,9 @@ func (p *ProjectManager) Create(project *models.Project) (int64, error) {
func (p *ProjectManager) Delete(projectIDOrName interface{}) error { func (p *ProjectManager) Delete(projectIDOrName interface{}) error {
id, ok := projectIDOrName.(int64) id, ok := projectIDOrName.(int64)
if !ok { if !ok {
project := p.Get(projectIDOrName) project, err := p.Get(projectIDOrName)
if project == nil { if err != nil {
return fmt.Errorf(fmt.Sprintf("project %v not found", projectIDOrName)) return err
} }
id = project.ProjectID id = project.ProjectID
} }
@ -176,41 +180,66 @@ func (p *ProjectManager) Update(projectIDOrName interface{},
project *models.Project) error { project *models.Project) error {
id, ok := projectIDOrName.(int64) id, ok := projectIDOrName.(int64)
if !ok { if !ok {
pro := p.Get(projectIDOrName) pro, err := p.Get(projectIDOrName)
if pro == nil { if err != nil {
return fmt.Errorf(fmt.Sprintf("project %v not found", projectIDOrName)) return err
} }
id = pro.ProjectID id = pro.ProjectID
} }
return dao.ToggleProjectPublicity(id, project.Public) return dao.ToggleProjectPublicity(id, project.Public)
} }
// GetAll ... // GetAll returns a project list according to the query parameters
func (p *ProjectManager) GetAll(owner, name, public, member string, func (p *ProjectManager) GetAll(query *projectmanager.QueryParam) (
role int, page, size int64) ([]*models.Project, int64) { []*models.Project, error) {
total, err := dao.GetTotalOfProjects(owner, name, public, member, role)
if err != nil { var (
log.Errorf("failed to get total of projects: %v", err) owner string
return []*models.Project{}, 0 name string
public string
member string
role int
page int64
size int64
)
if query != nil {
owner = query.Owner
name = query.Name
public = query.Public
if query.Member != nil {
member = query.Member.Name
role = query.Member.Role
}
if query.Pagination != nil {
page = query.Pagination.Page
size = query.Pagination.Size
}
} }
return filter(owner, name, public, member, role, page, size), total return dao.GetProjects(owner, name, public, member, role, page, size)
} }
func filter(owner, name, public, member string, // GetTotal returns the total count according to the query parameters
role int, page, size int64) []*models.Project { func (p *ProjectManager) GetTotal(query *projectmanager.QueryParam) (
projects := []*models.Project{} int64, error) {
var (
owner string
name string
public string
member string
role int
)
list, err := dao.GetProjects(owner, name, public, member, role, if query != nil {
page, size) owner = query.Owner
if err != nil { name = query.Name
log.Errorf("failed to get projects: %v", err) public = query.Public
return projects if query.Member != nil {
member = query.Member.Name
role = query.Member.Role
}
} }
if len(list) != 0 { return dao.GetTotalOfProjects(owner, name, public, member, role)
projects = append(projects, list...)
}
return projects
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/projectmanager"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -74,62 +75,76 @@ func TestGet(t *testing.T) {
pm := &ProjectManager{} pm := &ProjectManager{}
// project name // project name
project := pm.Get("library") project, err := pm.Get("library")
assert.Nil(t, err)
assert.NotNil(t, project) assert.NotNil(t, project)
assert.Equal(t, "library", project.Name) assert.Equal(t, "library", project.Name)
// project ID // project ID
project = pm.Get(int64(1)) project, err = pm.Get(int64(1))
assert.Nil(t, err)
assert.NotNil(t, project) assert.NotNil(t, project)
assert.Equal(t, int64(1), project.ProjectID) assert.Equal(t, int64(1), project.ProjectID)
// non-exist project // non-exist project
project = pm.Get("non-exist-project") project, err = pm.Get("non-exist-project")
assert.Nil(t, err)
assert.Nil(t, project) assert.Nil(t, project)
// invalid type // invalid type
project = pm.Get(true) project, err = pm.Get(true)
assert.Nil(t, project) assert.NotNil(t, err)
} }
func TestExist(t *testing.T) { func TestExist(t *testing.T) {
pm := &ProjectManager{} pm := &ProjectManager{}
// exist project // exist project
assert.True(t, pm.Exist("library")) exist, err := pm.Exist("library")
assert.Nil(t, err)
assert.True(t, exist)
// non-exist project // non-exist project
assert.False(t, pm.Exist("non-exist-project")) exist, err = pm.Exist("non-exist-project")
assert.Nil(t, err)
assert.False(t, exist)
} }
func TestIsPublic(t *testing.T) { func TestIsPublic(t *testing.T) {
pms := &ProjectManager{} pms := &ProjectManager{}
// public project // public project
assert.True(t, pms.IsPublic("library")) public, err := pms.IsPublic("library")
assert.Nil(t, err)
assert.True(t, public)
// non exist project // non exist project
assert.False(t, pms.IsPublic("non_exist_project")) public, err = pms.IsPublic("non_exist_project")
assert.Nil(t, err)
assert.False(t, public)
} }
func TestGetRoles(t *testing.T) { func TestGetRoles(t *testing.T) {
pm := &ProjectManager{} pm := &ProjectManager{}
// non exist user // non exist user
assert.Equal(t, []int{}, roles, err := pm.GetRoles("non_exist_user", int64(1))
pm.GetRoles("non_exist_user", int64(1))) assert.Nil(t, err)
assert.Equal(t, []int{}, roles)
// exist project // exist project
assert.Equal(t, []int{common.RoleProjectAdmin}, roles, err = pm.GetRoles("admin", "library")
pm.GetRoles("admin", "library")) assert.Nil(t, err)
assert.Equal(t, []int{common.RoleProjectAdmin}, roles)
// non-exist project // non-exist project
assert.Equal(t, []int{}, roles, err = pm.GetRoles("admin", "non_exist_project")
pm.GetRoles("admin", "non_exist_project")) assert.Nil(t, err)
assert.Equal(t, []int{}, roles)
} }
func TestGetPublic(t *testing.T) { func TestGetPublic(t *testing.T) {
pm := &ProjectManager{} pm := &ProjectManager{}
projects := pm.GetPublic() projects, err := pm.GetPublic()
assert.Nil(t, err)
assert.NotEqual(t, 0, len(projects)) assert.NotEqual(t, 0, len(projects))
for _, project := range projects { for _, project := range projects {
@ -139,7 +154,8 @@ func TestGetPublic(t *testing.T) {
func TestGetByMember(t *testing.T) { func TestGetByMember(t *testing.T) {
pm := &ProjectManager{} pm := &ProjectManager{}
projects := pm.GetByMember("admin") projects, err := pm.GetByMember("admin")
assert.Nil(t, err)
assert.NotEqual(t, 0, len(projects)) assert.NotEqual(t, 0, len(projects))
} }
@ -190,16 +206,51 @@ func TestUpdate(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
defer pm.Delete(id) defer pm.Delete(id)
project := pm.Get(id) project, err := pm.Get(id)
assert.Nil(t, err)
assert.Equal(t, 0, project.Public) assert.Equal(t, 0, project.Public)
project.Public = 1 project.Public = 1
assert.Nil(t, pm.Update(id, project)) assert.Nil(t, pm.Update(id, project))
project = pm.Get(id) project, err = pm.Get(id)
assert.Nil(t, err)
assert.Equal(t, 1, project.Public) assert.Equal(t, 1, project.Public)
} }
func TestGetTotal(t *testing.T) {
pm := &ProjectManager{}
id, err := pm.Create(&models.Project{
Name: "get_total_test",
OwnerID: 1,
Public: 1,
})
assert.Nil(t, err)
defer pm.Delete(id)
// get by name
total, err := pm.GetTotal(&projectmanager.QueryParam{
Name: "get_total_test",
})
assert.Nil(t, err)
assert.Equal(t, int64(1), total)
// get by owner
total, err = pm.GetTotal(&projectmanager.QueryParam{
Owner: "admin",
})
assert.Nil(t, err)
assert.NotEqual(t, 0, total)
// get by public
total, err = pm.GetTotal(&projectmanager.QueryParam{
Public: "true",
})
assert.Nil(t, err)
assert.NotEqual(t, 0, total)
}
func TestGetAll(t *testing.T) { func TestGetAll(t *testing.T) {
pm := &ProjectManager{} pm := &ProjectManager{}
@ -212,13 +263,17 @@ func TestGetAll(t *testing.T) {
defer pm.Delete(id) defer pm.Delete(id)
// get by name // get by name
projects, total := pm.GetAll("", "get_all_test", "", "", 0, 0, 0) projects, err := pm.GetAll(&projectmanager.QueryParam{
assert.Equal(t, int64(1), total) Name: "get_all_test",
})
assert.Nil(t, err)
assert.Equal(t, id, projects[0].ProjectID) assert.Equal(t, id, projects[0].ProjectID)
// get by owner // get by owner
projects, total = pm.GetAll("admin", "", "", "", 0, 0, 0) projects, err = pm.GetAll(&projectmanager.QueryParam{
assert.NotEqual(t, 0, total) Owner: "admin",
})
assert.Nil(t, err)
exist := false exist := false
for _, project := range projects { for _, project := range projects {
if project.ProjectID == id { if project.ProjectID == id {
@ -229,8 +284,10 @@ func TestGetAll(t *testing.T) {
assert.True(t, exist) assert.True(t, exist)
// get by public // get by public
projects, total = pm.GetAll("", "", "true", "", 0, 0, 0) projects, err = pm.GetAll(&projectmanager.QueryParam{
assert.NotEqual(t, 0, total) Public: "true",
})
assert.Nil(t, err)
exist = false exist = false
for _, project := range projects { for _, project := range projects {
if project.ProjectID == id { if project.ProjectID == id {

View File

@ -18,28 +18,43 @@ import (
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
) )
// QueryParam can be used to set query parameters when listing projects
type QueryParam struct {
Name string // the name of project
Owner string // the username of project owner
Public string // the project is public or not, can be "ture","false" and ""
Member *Member // the member of project
Pagination *Pagination // pagination information
}
// Member fitler by member's username and role
type Member struct {
Name string // the username of member
Role int // the role of the member has to the project
}
// Pagination ...
type Pagination struct {
Page int64
Size int64
}
// ProjectManager is the project mamager which abstracts the operations related // ProjectManager is the project mamager which abstracts the operations related
// to projects // to projects
type ProjectManager interface { type ProjectManager interface {
Get(projectIDOrName interface{}) *models.Project Get(projectIDOrName interface{}) (*models.Project, error)
IsPublic(projectIDOrName interface{}) bool IsPublic(projectIDOrName interface{}) (bool, error)
Exist(projectIDOrName interface{}) bool Exist(projectIDOrName interface{}) (bool, error)
GetRoles(username string, projectIDOrName interface{}) []int GetRoles(username string, projectIDOrName interface{}) ([]int, error)
// get all public project // get all public project
GetPublic() []*models.Project GetPublic() ([]*models.Project, error)
// get projects which the user is a member of // get projects which the user is a member of
GetByMember(username string) []*models.Project GetByMember(username string) ([]*models.Project, error)
Create(*models.Project) (int64, error) Create(*models.Project) (int64, error)
Delete(projectIDOrName interface{}) error Delete(projectIDOrName interface{}) error
Update(projectIDOrName interface{}, project *models.Project) error Update(projectIDOrName interface{}, project *models.Project) error
// GetAll returns a project list and the total count according to // GetAll returns a project list according to the query parameters
// the query conditions: GetAll(query *QueryParam) ([]*models.Project, error)
// owner: username of owner // GetTotal returns the total count according to the query parameters
// name: name of project GetTotal(query *QueryParam) (int64, error)
// public: public or not, can be "true", "false" or ""
// member: username of the member
// role: the role of member specified by member parameter
// page, size: pagination parameters
GetAll(owner, name, public, member string, role int, page,
size int64) ([]*models.Project, int64)
} }