From b5279ea1f1836786aa911fa984ac1a39cb618873 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 17 May 2017 17:15:43 +0800 Subject: [PATCH] update --- src/common/security/rbac/context.go | 60 +++++- src/common/security/rbac/context_test.go | 41 +++-- src/ui/api/project.go | 222 +++++++++++------------ src/ui/api/repository.go | 66 ++++++- src/ui/projectmanager/db/pm.go | 161 +++++++++------- src/ui/projectmanager/db/pm_test.go | 109 ++++++++--- src/ui/projectmanager/pm.go | 47 +++-- 7 files changed, 445 insertions(+), 261 deletions(-) diff --git a/src/common/security/rbac/context.go b/src/common/security/rbac/context.go index 85e00c52d..9c13e4a28 100644 --- a/src/common/security/rbac/context.go +++ b/src/common/security/rbac/context.go @@ -17,6 +17,7 @@ package rbac import ( "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/models" + "github.com/vmware/harbor/src/common/utils/log" "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 func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool { // 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 } // 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 } @@ -79,7 +93,13 @@ func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool { 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 { switch role { case common.RoleProjectAdmin, @@ -99,7 +119,14 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool { } // 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 } @@ -108,7 +135,13 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool { 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 { switch role { case common.RoleProjectAdmin, @@ -127,7 +160,14 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool { } // 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 } @@ -136,7 +176,13 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool { 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 { switch role { case common.RoleProjectAdmin: diff --git a/src/common/security/rbac/context_test.go b/src/common/security/rbac/context_test.go index 371530871..9d94998d2 100644 --- a/src/common/security/rbac/context_test.go +++ b/src/common/security/rbac/context_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/models" + "github.com/vmware/harbor/src/ui/projectmanager" ) var ( @@ -52,42 +53,42 @@ type fakePM struct { roles map[string][]int } -func (f *fakePM) IsPublic(projectIDOrName interface{}) bool { +func (f *fakePM) IsPublic(projectIDOrName interface{}) (bool, error) { for _, project := range f.projects { 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 { - return f.roles[projectIDOrName.(string)] +func (f *fakePM) GetRoles(username string, projectIDOrName interface{}) ([]int, error) { + 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 { 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 { if project.Name == projectIDOrName.(string) { - return true + return true, nil } } - return false + return false, nil } // nil implement -func (f *fakePM) GetPublic() []*models.Project { - return []*models.Project{} +func (f *fakePM) GetPublic() ([]*models.Project, error) { + return []*models.Project{}, nil } // nil implement -func (f *fakePM) GetByMember(username string) []*models.Project { - return []*models.Project{} +func (f *fakePM) GetByMember(username string) ([]*models.Project, error) { + return []*models.Project{}, nil } // nil implement @@ -106,9 +107,13 @@ func (f *fakePM) Update(projectIDOrName interface{}, project *models.Project) er } // nil implement -func (f *fakePM) GetAll(owner, name, public, member string, role int, page, - size int64) ([]*models.Project, int64) { - return []*models.Project{}, 0 +func (f *fakePM) GetAll(*projectmanager.QueryParam) ([]*models.Project, error) { + return []*models.Project{}, nil +} + +// nil implement +func (f *fakePM) GetTotal(*projectmanager.QueryParam) (int64, error) { + return 0, nil } func TestIsAuthenticated(t *testing.T) { diff --git a/src/ui/api/project.go b/src/ui/api/project.go index 5505b5ee0..92b301470 100644 --- a/src/ui/api/project.go +++ b/src/ui/api/project.go @@ -24,6 +24,7 @@ import ( "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/ui/config" + "github.com/vmware/harbor/src/ui/projectmanager" "strconv" "time" @@ -32,6 +33,7 @@ import ( // ProjectAPI handles request to /api/projects/{} /api/projects/{}/logs type ProjectAPI struct { BaseController + project *models.Project } type projectReq struct { @@ -44,6 +46,38 @@ const projectNameMinLen int = 2 const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*` 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 ... func (p *ProjectAPI) Post() { if !p.SecurityCtx.IsAuthenticated() { @@ -70,7 +104,13 @@ func (p *ProjectAPI) Post() { 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, "") return } @@ -117,7 +157,13 @@ func (p *ProjectAPI) Head() { 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 { p.HandleNotFound(fmt.Sprintf("project %s not found", name)) return @@ -126,99 +172,63 @@ func (p *ProjectAPI) Head() { // Get ... func (p *ProjectAPI) Get() { - 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 project.Public == 0 { + if p.project.Public == 0 { if !p.SecurityCtx.IsAuthenticated() { p.HandleUnauthorized() return } - if !p.SecurityCtx.HasReadPerm(id) { + if !p.SecurityCtx.HasReadPerm(p.project.ProjectID) { p.HandleForbidden(p.SecurityCtx.GetUsername()) return } } - p.Data["json"] = project + p.Data["json"] = p.project p.ServeJSON() } // 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() { p.HandleUnauthorized() return } - if !p.SecurityCtx.HasAllPerm(id) { + if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) { p.HandleForbidden(p.SecurityCtx.GetUsername()) return } - contains, err := projectContainsRepo(project.Name) + contains, err := projectContainsRepo(p.project.Name) 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, "") } if contains { p.CustomAbort(http.StatusPreconditionFailed, "project contains repositores, can not be deleted") } - contains, err = projectContainsPolicy(id) + contains, err = projectContainsPolicy(p.project.ProjectID) 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, "") } if contains { 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( - fmt.Sprintf("failed to delete project %d: %v", id, err)) + fmt.Sprintf("failed to delete project %d: %v", p.project.ProjectID, err)) return } go func() { if err := dao.AddAccessLog(models.AccessLog{ Username: p.SecurityCtx.GetUsername(), - ProjectID: id, - RepoName: project.Name + "/", + ProjectID: p.project.ProjectID, + RepoName: p.project.Name + "/", RepoTag: "N/A", Operation: "delete", OpTime: time.Now(), @@ -250,53 +260,61 @@ func projectContainsPolicy(id int64) (bool, error) { // TODO refacter pattern to: // /api/repositories?owner=xxx&name=xxx&public=true&member=xxx&role=1&page=1&size=3 func (p *ProjectAPI) List() { + query := &projectmanager.QueryParam{} - // query conditions: - var ( - 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") + query.Name = p.GetString("project_name") + public := p.GetString("is_public") if len(public) != 0 { if public != "0" && public != "1" { p.HandleBadRequest("is_public should be 0 or 1") return } if public == "1" { - public = "true" + query.Public = "true" } } - page, size = p.GetPaginationParams() - - if public != "true" { + if query.Public != "true" { //if the request is not for public projects, user must login or provide credential if !p.SecurityCtx.IsAuthenticated() { p.HandleUnauthorized() return } - public = "" - member = p.SecurityCtx.GetUsername() - - if p.SecurityCtx.IsSysAdmin() { - member = "" + if !p.SecurityCtx.IsSysAdmin() { + query.Member = &projectmanager.Member{ + Name: p.SecurityCtx.GetUsername(), + } } } - projects, total := p.ProjectMgr.GetAll(owner, name, public, member, - role, page, size) + total, err := p.ProjectMgr.GetTotal(query) + 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 { - if public != "true" { - roles := p.ProjectMgr.GetRoles(p.SecurityCtx.GetUsername(), project.ProjectID) + if query.Public != "true" { + 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 { project.Role = roles[0] } @@ -323,30 +341,12 @@ func (p *ProjectAPI) List() { // 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() { p.HandleUnauthorized() return } - if !p.SecurityCtx.HasAllPerm(id) { + if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) { p.HandleForbidden(p.SecurityCtx.GetUsername()) return } @@ -358,40 +358,24 @@ func (p *ProjectAPI) ToggleProjectPublic() { return } - if err := p.ProjectMgr.Update(id, &models.Project{ - Public: req.Public, - }); err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to update project %d: %v", id, err)) + if err := p.ProjectMgr.Update(p.project.ProjectID, + &models.Project{ + Public: req.Public, + }); err != nil { + p.HandleInternalServerError(fmt.Sprintf("failed to update project %d: %v", + p.project.ProjectID, err)) return } } // FilterAccessLog handles GET to /api/projects/{}/logs 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() { p.HandleUnauthorized() return } - if !p.SecurityCtx.HasReadPerm(id) { + if !p.SecurityCtx.HasReadPerm(p.project.ProjectID) { p.HandleForbidden(p.SecurityCtx.GetUsername()) return } @@ -399,7 +383,7 @@ func (p *ProjectAPI) FilterAccessLog() { var query models.AccessLog p.DecodeJSONReq(&query) - query.ProjectID = id + query.ProjectID = p.project.ProjectID query.BeginTime = time.Unix(query.BeginTimestamp, 0) query.EndTime = time.Unix(query.EndTimestamp, 0) diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index f16ef090f..7dc036e63 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -69,7 +69,14 @@ func (ra *RepositoryAPI) Get() { 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)) return } @@ -146,7 +153,14 @@ func (ra *RepositoryAPI) Delete() { repoName := ra.GetString(":splat") 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)) return } @@ -236,7 +250,18 @@ func (ra *RepositoryAPI) Delete() { go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) 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{ Username: ra.SecurityCtx.GetUsername(), ProjectID: project.ProjectID, @@ -250,7 +275,7 @@ func (ra *RepositoryAPI) Delete() { }(t) } - exist, err := repositoryExist(repoName, rc) + exist, err = repositoryExist(repoName, rc) if err != nil { log.Errorf("failed to check the existence of repository %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "") @@ -268,7 +293,14 @@ func (ra *RepositoryAPI) GetTags() { repoName := ra.GetString(":splat") 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)) return } @@ -360,7 +392,14 @@ func (ra *RepositoryAPI) GetManifests() { } 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)) return } @@ -455,10 +494,19 @@ func (ra *RepositoryAPI) GetTopRepos() { } 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() { - projects = append(projects, ra.ProjectMgr.GetByMember( - ra.SecurityCtx.GetUsername())...) + list, err := ra.ProjectMgr.GetByMember(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 { diff --git a/src/ui/projectmanager/db/pm.go b/src/ui/projectmanager/db/pm.go index d90342119..5c0446d94 100644 --- a/src/ui/projectmanager/db/pm.go +++ b/src/ui/projectmanager/db/pm.go @@ -21,77 +21,74 @@ 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" ) // ProjectManager implements pm.PM interface based on database type ProjectManager struct{} // Get ... -func (p *ProjectManager) Get(projectIDOrName interface{}) *models.Project { +func (p *ProjectManager) Get(projectIDOrName interface{}) ( + *models.Project, error) { switch projectIDOrName.(type) { case string: - name := projectIDOrName.(string) - project, err := dao.GetProjectByName(name) - if err != nil { - log.Errorf("failed to get project %s: %v", name, err) - return nil - } - return project + return dao.GetProjectByName(projectIDOrName.(string)) case int64: - id := projectIDOrName.(int64) - project, err := dao.GetProjectByID(id) - if err != nil { - log.Errorf("failed to get project %d: %v", id, err) - return nil - } - return project + return dao.GetProjectByID(projectIDOrName.(int64)) default: - log.Errorf("unsupported type of %v, must be string or int64", projectIDOrName) - return nil + return nil, fmt.Errorf("unsupported type of %v, must be string or int64", projectIDOrName) } } // Exist ... -func (p *ProjectManager) Exist(projectIDOrName interface{}) bool { - return p.Get(projectIDOrName) != nil +func (p *ProjectManager) Exist(projectIDOrName interface{}) (bool, error) { + project, err := p.Get(projectIDOrName) + if err != nil { + return false, err + } + return project != nil, nil } // IsPublic returns whether the project is public or not -func (p *ProjectManager) IsPublic(projectIDOrName interface{}) bool { - project := p.Get(projectIDOrName) - if project == nil { - return false +func (p *ProjectManager) IsPublic(projectIDOrName interface{}) (bool, error) { + project, err := p.Get(projectIDOrName) + if err != nil { + 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 -func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) []int { +func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) ([]int, error) { roles := []int{} user, err := dao.GetUser(models.User{ Username: username, }) if err != nil { - log.Errorf("failed to get user %s: %v", username, err) - return roles + return nil, fmt.Errorf("failed to get user %s: %v", + username, err) } 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 { - return roles + return roles, nil } roleList, err := dao.GetUserProjectRoles(user.UserID, project.ProjectID) if err != nil { - log.Errorf("failed to get roles for user %d to project %d: %v", - user.UserID, project.ProjectID, err) - return roles + return nil, err } 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 -func (p *ProjectManager) GetPublic() []*models.Project { - return filter("", "", "true", "", 0, 0, 0) +func (p *ProjectManager) GetPublic() ([]*models.Project, error) { + return p.GetAll(&projectmanager.QueryParam{ + Public: "true", + }) } // GetByMember returns all projects which the user is a member of -func (p *ProjectManager) GetByMember(username string) []*models.Project { - return filter("", "", "", username, 0, 0, 0) +func (p *ProjectManager) GetByMember(username string) ( + []*models.Project, error) { + return p.GetAll(&projectmanager.QueryParam{ + Member: &projectmanager.Member{ + Name: username, + }, + }) } // Create ... @@ -161,9 +165,9 @@ func (p *ProjectManager) Create(project *models.Project) (int64, error) { func (p *ProjectManager) Delete(projectIDOrName interface{}) error { id, ok := projectIDOrName.(int64) if !ok { - project := p.Get(projectIDOrName) - if project == nil { - return fmt.Errorf(fmt.Sprintf("project %v not found", projectIDOrName)) + project, err := p.Get(projectIDOrName) + if err != nil { + return err } id = project.ProjectID } @@ -176,41 +180,66 @@ func (p *ProjectManager) Update(projectIDOrName interface{}, project *models.Project) error { id, ok := projectIDOrName.(int64) if !ok { - pro := p.Get(projectIDOrName) - if pro == nil { - return fmt.Errorf(fmt.Sprintf("project %v not found", projectIDOrName)) + pro, err := p.Get(projectIDOrName) + if err != nil { + return err } id = pro.ProjectID } return dao.ToggleProjectPublicity(id, project.Public) } -// GetAll ... -func (p *ProjectManager) GetAll(owner, name, public, member string, - role int, page, size int64) ([]*models.Project, int64) { - total, err := dao.GetTotalOfProjects(owner, name, public, member, role) - if err != nil { - log.Errorf("failed to get total of projects: %v", err) - return []*models.Project{}, 0 +// GetAll returns a project list according to the query parameters +func (p *ProjectManager) GetAll(query *projectmanager.QueryParam) ( + []*models.Project, error) { + + var ( + owner string + 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, - role int, page, size int64) []*models.Project { - projects := []*models.Project{} +// GetTotal returns the total count according to the query parameters +func (p *ProjectManager) GetTotal(query *projectmanager.QueryParam) ( + int64, error) { + var ( + owner string + name string + public string + member string + role int + ) - list, err := dao.GetProjects(owner, name, public, member, role, - page, size) - if err != nil { - log.Errorf("failed to get projects: %v", err) - return projects + 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 len(list) != 0 { - projects = append(projects, list...) - } - - return projects + return dao.GetTotalOfProjects(owner, name, public, member, role) } diff --git a/src/ui/projectmanager/db/pm_test.go b/src/ui/projectmanager/db/pm_test.go index 621de06ad..91519fa24 100644 --- a/src/ui/projectmanager/db/pm_test.go +++ b/src/ui/projectmanager/db/pm_test.go @@ -24,6 +24,7 @@ import ( "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" ) func TestMain(m *testing.M) { @@ -74,62 +75,76 @@ func TestGet(t *testing.T) { pm := &ProjectManager{} // project name - project := pm.Get("library") + project, err := pm.Get("library") + assert.Nil(t, err) assert.NotNil(t, project) assert.Equal(t, "library", project.Name) // project ID - project = pm.Get(int64(1)) + project, err = pm.Get(int64(1)) + assert.Nil(t, err) assert.NotNil(t, project) assert.Equal(t, int64(1), project.ProjectID) // non-exist project - project = pm.Get("non-exist-project") + project, err = pm.Get("non-exist-project") + assert.Nil(t, err) assert.Nil(t, project) // invalid type - project = pm.Get(true) - assert.Nil(t, project) + project, err = pm.Get(true) + assert.NotNil(t, err) } func TestExist(t *testing.T) { pm := &ProjectManager{} // exist project - assert.True(t, pm.Exist("library")) + exist, err := pm.Exist("library") + assert.Nil(t, err) + assert.True(t, exist) // 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) { pms := &ProjectManager{} // public project - assert.True(t, pms.IsPublic("library")) + public, err := pms.IsPublic("library") + assert.Nil(t, err) + assert.True(t, public) // 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) { pm := &ProjectManager{} // non exist user - assert.Equal(t, []int{}, - pm.GetRoles("non_exist_user", int64(1))) + roles, err := pm.GetRoles("non_exist_user", int64(1)) + assert.Nil(t, err) + assert.Equal(t, []int{}, roles) // exist project - assert.Equal(t, []int{common.RoleProjectAdmin}, - pm.GetRoles("admin", "library")) + roles, err = pm.GetRoles("admin", "library") + assert.Nil(t, err) + assert.Equal(t, []int{common.RoleProjectAdmin}, roles) // non-exist project - assert.Equal(t, []int{}, - pm.GetRoles("admin", "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 := pm.GetPublic() - + projects, err := pm.GetPublic() + assert.Nil(t, err) assert.NotEqual(t, 0, len(projects)) for _, project := range projects { @@ -139,7 +154,8 @@ func TestGetPublic(t *testing.T) { func TestGetByMember(t *testing.T) { pm := &ProjectManager{} - projects := pm.GetByMember("admin") + projects, err := pm.GetByMember("admin") + assert.Nil(t, err) assert.NotEqual(t, 0, len(projects)) } @@ -190,16 +206,51 @@ func TestUpdate(t *testing.T) { assert.Nil(t, err) defer pm.Delete(id) - project := pm.Get(id) + project, err := pm.Get(id) + assert.Nil(t, err) assert.Equal(t, 0, project.Public) project.Public = 1 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) } +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) { pm := &ProjectManager{} @@ -212,13 +263,17 @@ func TestGetAll(t *testing.T) { defer pm.Delete(id) // get by name - projects, total := pm.GetAll("", "get_all_test", "", "", 0, 0, 0) - assert.Equal(t, int64(1), total) + projects, err := pm.GetAll(&projectmanager.QueryParam{ + Name: "get_all_test", + }) + assert.Nil(t, err) assert.Equal(t, id, projects[0].ProjectID) // get by owner - projects, total = pm.GetAll("admin", "", "", "", 0, 0, 0) - assert.NotEqual(t, 0, total) + projects, err = pm.GetAll(&projectmanager.QueryParam{ + Owner: "admin", + }) + assert.Nil(t, err) exist := false for _, project := range projects { if project.ProjectID == id { @@ -229,8 +284,10 @@ func TestGetAll(t *testing.T) { assert.True(t, exist) // get by public - projects, total = pm.GetAll("", "", "true", "", 0, 0, 0) - assert.NotEqual(t, 0, total) + projects, err = pm.GetAll(&projectmanager.QueryParam{ + Public: "true", + }) + assert.Nil(t, err) exist = false for _, project := range projects { if project.ProjectID == id { diff --git a/src/ui/projectmanager/pm.go b/src/ui/projectmanager/pm.go index 87fb966b0..1b80a4282 100644 --- a/src/ui/projectmanager/pm.go +++ b/src/ui/projectmanager/pm.go @@ -18,28 +18,43 @@ import ( "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 // to projects type ProjectManager interface { - Get(projectIDOrName interface{}) *models.Project - IsPublic(projectIDOrName interface{}) bool - Exist(projectIDOrName interface{}) bool - GetRoles(username string, projectIDOrName interface{}) []int + 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 + GetPublic() ([]*models.Project, error) // 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) Delete(projectIDOrName interface{}) error Update(projectIDOrName interface{}, project *models.Project) error - // GetAll returns a project list and the total count according to - // the query conditions: - // owner: username of owner - // name: name of project - // 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) + // GetAll returns a project list according to the query parameters + GetAll(query *QueryParam) ([]*models.Project, error) + // GetTotal returns the total count according to the query parameters + GetTotal(query *QueryParam) (int64, error) }