diff --git a/src/common/api/base.go b/src/common/api/base.go index 40f4b98b4..879656adb 100644 --- a/src/common/api/base.go +++ b/src/common/api/base.go @@ -39,6 +39,36 @@ type BaseAPI struct { beego.Controller } +// HandleNotFound ... +func (b *BaseAPI) HandleNotFound(text string) { + log.Info(text) + b.RenderError(http.StatusNotFound, text) +} + +// HandleUnauthorized ... +func (b *BaseAPI) HandleUnauthorized() { + log.Info("unauthorized") + b.RenderError(http.StatusUnauthorized, "") +} + +// HandleForbidden ... +func (b *BaseAPI) HandleForbidden(username string) { + log.Info("forbidden: %s", username) + b.RenderError(http.StatusForbidden, "") +} + +// HandleBadRequest ... +func (b *BaseAPI) HandleBadRequest(text string) { + log.Info(text) + b.RenderError(http.StatusBadRequest, text) +} + +// HandleInternalServerError ... +func (b *BaseAPI) HandleInternalServerError(text string) { + log.Error(text) + b.RenderError(http.StatusInternalServerError, "") +} + // Render returns nil as it won't render template func (b *BaseAPI) Render() error { return nil @@ -83,6 +113,7 @@ func (b *BaseAPI) DecodeJSONReqAndValidate(v interface{}) { } // ValidateUser checks if the request triggered by a valid user +// TODO remove func (b *BaseAPI) ValidateUser() int { userID, needsCheck, ok := b.GetUserIDForRequest() if !ok { diff --git a/src/common/dao/repository.go b/src/common/dao/repository.go index b0d5c1f24..4cd8ec630 100644 --- a/src/common/dao/repository.go +++ b/src/common/dao/repository.go @@ -104,27 +104,13 @@ func GetRepositoryByProjectName(name string) ([]*models.RepoRecord, error) { } //GetTopRepos returns the most popular repositories -func GetTopRepos(userID int, count int) ([]*models.RepoRecord, error) { - sql := - `select r.repository_id, r.name, - r.project_id, r.description, r.pull_count, - r.star_count, r.creation_time, r.update_time - from repository r - inner join project p on r.project_id = p.project_id - where ( - p.deleted = 0 and ( - p.public = 1 or ( - ? <> ? and ( - exists ( - select 1 from user u - where u.user_id = ? and u.sysadmin_flag = 1 - ) or exists ( - select 1 from project_member pm - where pm.project_id = p.project_id and pm.user_id = ? - ))))) - order by r.pull_count desc, r.name limit ?` +func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) { repositories := []*models.RepoRecord{} - _, err := GetOrmer().Raw(sql, userID, NonExistUserID, userID, userID, count).QueryRows(&repositories) + _, err := GetOrmer().QueryTable(&models.RepoRecord{}). + Filter("project_id__in", projectIDs). + OrderBy("-pull_count"). + Limit(n). + All(&repositories) return repositories, err } diff --git a/src/common/models/toprepo.go b/src/common/models/toprepo.go index 14aaf9f9e..275f72bbb 100644 --- a/src/common/models/toprepo.go +++ b/src/common/models/toprepo.go @@ -13,10 +13,3 @@ // limitations under the License. package models - -// TopRepo holds information about repository that accessed most -type TopRepo struct { - RepoName string `json:"name"` - AccessCount int64 `json:"count"` - // Creator string `json:"creator"` -} diff --git a/src/ui/api/base.go b/src/ui/api/base.go new file mode 100644 index 000000000..bd62f1a0d --- /dev/null +++ b/src/ui/api/base.go @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +import ( + "net/http" + + "github.com/vmware/harbor/src/common/api" + "github.com/vmware/harbor/src/common/security" + "github.com/vmware/harbor/src/common/utils/log" + "github.com/vmware/harbor/src/ui/filter" + "github.com/vmware/harbor/src/ui/projectmanager" +) + +// BaseController ... +type BaseController struct { + api.BaseAPI + // SecurityCxt is the security context used to authN &authZ + SecurityCxt security.Context + // ProManager is the project manager which abstracts the operations + // related to projects + ProManager projectmanager.ProjectManager +} + +// Prepare inits security context and project manager from beego +// context +func (b *BaseController) Prepare() { + ok := false + ctx := b.Ctx.Input.GetData(filter.HarborSecurityContext) + b.SecurityCxt, ok = ctx.(security.Context) + if !ok { + log.Error("failed to get security context") + b.CustomAbort(http.StatusInternalServerError, "") + } + + pm := b.Ctx.Input.GetData(filter.HarborProjectManager) + b.ProManager, ok = pm.(projectmanager.ProjectManager) + if !ok { + log.Error("failed to get project manager") + b.CustomAbort(http.StatusInternalServerError, "") + } +} diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index 07561021e..f4bf94f8f 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -23,23 +23,20 @@ import ( "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema2" - "github.com/vmware/harbor/src/common/api" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/notary" "github.com/vmware/harbor/src/common/utils/registry" - "github.com/vmware/harbor/src/common/utils/registry/auth" registry_error "github.com/vmware/harbor/src/common/utils/registry/error" "github.com/vmware/harbor/src/ui/config" - svc_utils "github.com/vmware/harbor/src/ui/service/utils" ) // RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put // in the query string as the web framework can not parse the URL if it contains veriadic sectors. type RepositoryAPI struct { - api.BaseAPI + BaseController } type repoResp struct { @@ -54,7 +51,7 @@ type repoResp struct { UpdateTime time.Time `json:"update_time"` } -type detailedTagResp struct { +type tagResp struct { Tag string `json:"tag"` Manifest interface{} `json:"manifest"` } @@ -68,30 +65,24 @@ type manifestResp struct { func (ra *RepositoryAPI) Get() { projectID, err := ra.GetInt64("project_id") if err != nil || projectID <= 0 { - ra.CustomAbort(http.StatusBadRequest, "invalid project_id") + ra.HandleBadRequest(fmt.Sprintf("invalid project_id %s", ra.GetString("project_id"))) + return } - project, err := dao.GetProjectByID(projectID) - if err != nil { - log.Errorf("failed to get project %d: %v", projectID, err) - ra.CustomAbort(http.StatusInternalServerError, "") + if !ra.ProManager.Exist(projectID) { + ra.HandleNotFound(fmt.Sprintf("project %d not found", projectID)) + return } - if project == nil { - ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %d not found", projectID)) - } - - if project.Public == 0 { - var userID int - - if svc_utils.VerifySecret(ra.Ctx.Request, config.JobserviceSecret()) { - userID = 1 - } else { - userID = ra.ValidateUser() + if !ra.ProManager.IsPublic(projectID) { + if !ra.SecurityCxt.IsAuthenticated() { + ra.HandleUnauthorized() + return } - if !checkProjectPermission(userID, projectID) { - ra.CustomAbort(http.StatusForbidden, "") + if !ra.SecurityCxt.HasReadPerm(projectID) { + ra.HandleForbidden(ra.SecurityCxt.GetUsername()) + return } } @@ -99,19 +90,18 @@ func (ra *RepositoryAPI) Get() { total, err := dao.GetTotalOfRepositoriesByProject(projectID, keyword) if err != nil { - log.Errorf("failed to get total of repositories of project %d: %v", projectID, err) - ra.CustomAbort(http.StatusInternalServerError, "") + ra.HandleInternalServerError(fmt.Sprintf("failed to get total of repositories of project %d: %v", + projectID, err)) + return } page, pageSize := ra.GetPaginationParams() - detail := ra.GetString("detail") == "1" || ra.GetString("detail") == "true" - repositories, err := getRepositories(projectID, - keyword, pageSize, pageSize*(page-1), detail) + keyword, pageSize, pageSize*(page-1)) if err != nil { - log.Errorf("failed to get repository: %v", err) - ra.CustomAbort(http.StatusInternalServerError, "") + ra.HandleInternalServerError(fmt.Sprintf("failed to get repository: %v", err)) + return } ra.SetPaginationHeader(total, page, pageSize) @@ -120,21 +110,12 @@ func (ra *RepositoryAPI) Get() { } func getRepositories(projectID int64, keyword string, - limit, offset int64, detail bool) (interface{}, error) { + limit, offset int64) ([]*repoResp, error) { repositories, err := dao.GetRepositoriesByProject(projectID, keyword, limit, offset) if err != nil { return nil, err } - //keep compatibility with old API - if !detail { - result := []string{} - for _, repository := range repositories { - result = append(result, repository.Name) - } - return result, nil - } - return populateTagsCount(repositories) } @@ -168,22 +149,22 @@ func (ra *RepositoryAPI) Delete() { repoName := ra.GetString(":splat") projectName, _ := utils.ParseRepository(repoName) - project, err := dao.GetProjectByName(projectName) - if err != nil { - log.Errorf("failed to get project %s: %v", projectName, err) - ra.CustomAbort(http.StatusInternalServerError, "") + if !ra.ProManager.Exist(projectName) { + ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) + return } - if project == nil { - ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) + if !ra.SecurityCxt.IsAuthenticated() { + ra.HandleUnauthorized() + return } - userID := ra.ValidateUser() - if !hasProjectAdminRole(userID, project.ProjectID) { - ra.CustomAbort(http.StatusForbidden, "") + if !ra.SecurityCxt.HasAllPerm(projectName) { + ra.HandleForbidden(ra.SecurityCxt.GetUsername()) + return } - rc, err := ra.initRepositoryClient(repoName) + rc, err := ra.initRepositoryClient(ra.SecurityCxt.GetUsername(), repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") @@ -212,18 +193,11 @@ func (ra *RepositoryAPI) Delete() { tags = append(tags, tag) } - user, _, ok := ra.Ctx.Request.BasicAuth() - if !ok { - user, err = ra.getUsername() - if err != nil { - log.Errorf("failed to get user: %v", err) - } - } - if config.WithNotary() { var digest string signedTags := make(map[string]struct{}) - targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(), user, repoName) + targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(), + ra.SecurityCxt.GetUsername(), repoName) if err != nil { log.Errorf("Failed to get Notary targets for repository: %s, error: %v", repoName, err) log.Warningf("Failed to check signature status of repository: %s for deletion, there maybe orphaned targets in Notary.", repoName) @@ -243,7 +217,7 @@ func (ra *RepositoryAPI) Delete() { ra.CustomAbort(http.StatusInternalServerError, err.Error()) } log.Debugf("Tag: %s, digest: %s", t, digest) - if _, ok = signedTags[digest]; ok { + if _, ok := signedTags[digest]; ok { log.Errorf("Found signed tag, repostory: %s, tag: %s, deletion will be canceled", repoName, t) ra.CustomAbort(http.StatusPreconditionFailed, fmt.Sprintf("tag %s is signed", t)) } @@ -265,7 +239,8 @@ func (ra *RepositoryAPI) Delete() { go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) go func(tag string) { - if err := dao.AccessLog(user, projectName, repoName, tag, "delete"); err != nil { + if err := dao.AccessLog(ra.SecurityCxt.GetUsername(), + projectName, repoName, tag, "delete"); err != nil { log.Errorf("failed to add access log: %v", err) } }(t) @@ -284,35 +259,29 @@ func (ra *RepositoryAPI) Delete() { } } -type tag struct { - Name string `json:"name"` - Tags []string `json:"tags"` -} - // GetTags returns tags of a repository func (ra *RepositoryAPI) GetTags() { repoName := ra.GetString(":splat") - detail := ra.GetString("detail") == "1" || ra.GetString("detail") == "true" projectName, _ := utils.ParseRepository(repoName) - project, err := dao.GetProjectByName(projectName) - if err != nil { - log.Errorf("failed to get project %s: %v", projectName, err) - ra.CustomAbort(http.StatusInternalServerError, "") + if !ra.ProManager.Exist(projectName) { + ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) + return } - if project == nil { - ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) - } + if !ra.ProManager.IsPublic(projectName) { + if !ra.SecurityCxt.IsAuthenticated() { + ra.HandleUnauthorized() + return + } - if project.Public == 0 { - userID := ra.ValidateUser() - if !checkProjectPermission(userID, project.ProjectID) { - ra.CustomAbort(http.StatusForbidden, "") + if !ra.SecurityCxt.HasReadPerm(projectName) { + ra.HandleForbidden(ra.SecurityCxt.GetUsername()) + return } } - client, err := ra.initRepositoryClient(repoName) + client, err := ra.initRepositoryClient(ra.SecurityCxt.GetUsername(), repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") @@ -328,13 +297,7 @@ func (ra *RepositoryAPI) GetTags() { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } - if !detail { - ra.Data["json"] = tags - ra.ServeJSON() - return - } - - result := []detailedTagResp{} + result := []tagResp{} for _, tag := range tags { manifest, err := getManifest(client, tag, "v1") @@ -347,7 +310,7 @@ func (ra *RepositoryAPI) GetTags() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } - result = append(result, detailedTagResp{ + result = append(result, tagResp{ Tag: tag, Manifest: manifest.Manifest, }) @@ -355,7 +318,6 @@ func (ra *RepositoryAPI) GetTags() { ra.Data["json"] = result ra.ServeJSON() - } func listTag(client *registry.Repository) ([]string, error) { @@ -397,24 +359,24 @@ func (ra *RepositoryAPI) GetManifests() { } projectName, _ := utils.ParseRepository(repoName) - project, err := dao.GetProjectByName(projectName) - if err != nil { - log.Errorf("failed to get project %s: %v", projectName, err) - ra.CustomAbort(http.StatusInternalServerError, "") + if !ra.ProManager.Exist(projectName) { + ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) + return } - if project == nil { - ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) - } + if !ra.ProManager.IsPublic(projectName) { + if !ra.SecurityCxt.IsAuthenticated() { + ra.HandleUnauthorized() + return + } - if project.Public == 0 { - userID := ra.ValidateUser() - if !checkProjectPermission(userID, project.ProjectID) { - ra.CustomAbort(http.StatusForbidden, "") + if !ra.SecurityCxt.HasReadPerm(projectName) { + ra.HandleForbidden(ra.SecurityCxt.GetUsername()) + return } } - rc, err := ra.initRepositoryClient(repoName) + rc, err := ra.initRepositoryClient(ra.SecurityCxt.GetUsername(), repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") @@ -476,63 +438,16 @@ func getManifest(client *registry.Repository, return result, nil } -func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) { +func (ra *RepositoryAPI) initRepositoryClient(username, repoName string) (r *registry.Repository, err error) { endpoint, err := config.RegistryURL() if err != nil { return nil, err } - verify, err := config.VerifyRemoteCert() - if err != nil { - return nil, err - } - - username, password, ok := ra.Ctx.Request.BasicAuth() - if ok { - return newRepositoryClient(endpoint, !verify, username, password, - repoName, "repository", repoName, "pull", "push", "*") - } - - username, err = ra.getUsername() - if err != nil { - return nil, err - } - - return NewRepositoryClient(endpoint, !verify, username, repoName, + return NewRepositoryClient(endpoint, true, username, repoName, "repository", repoName, "pull", "push", "*") } -func (ra *RepositoryAPI) getUsername() (string, error) { - // get username from session - sessionUsername := ra.GetSession("username") - if sessionUsername != nil { - username, ok := sessionUsername.(string) - if ok { - return username, nil - } - } - - // if username does not exist in session, try to get userId from sessiion - // and then get username from DB according to the userId - sessionUserID := ra.GetSession("userId") - if sessionUserID != nil { - userID, ok := sessionUserID.(int) - if ok { - u := models.User{ - UserID: userID, - } - user, err := dao.GetUser(u) - if err != nil { - return "", err - } - - return user.Username, nil - } - } - - return "", nil -} - //GetTopRepos returns the most populor repositories func (ra *RepositoryAPI) GetTopRepos() { count, err := ra.GetInt("count", 10) @@ -540,33 +455,23 @@ func (ra *RepositoryAPI) GetTopRepos() { ra.CustomAbort(http.StatusBadRequest, "invalid count") } - userID, _, ok := ra.GetUserIDForRequest() - if !ok { - userID = dao.NonExistUserID + projectIDs := []int64{} + projects := ra.ProManager.GetPublic() + if ra.SecurityCxt.IsAuthenticated() { + projects = append(projects, ra.ProManager.GetByMember( + ra.SecurityCxt.GetUsername())...) } - repos, err := dao.GetTopRepos(userID, count) + for _, project := range projects { + projectIDs = append(projectIDs, project.ProjectID) + } + + repos, err := dao.GetTopRepos(projectIDs, count) if err != nil { log.Errorf("failed to get top repos: %v", err) ra.CustomAbort(http.StatusInternalServerError, "internal server error") } - detail := ra.GetString("detail") == "1" || ra.GetString("detail") == "true" - if !detail { - result := []*models.TopRepo{} - - for _, repo := range repos { - result = append(result, &models.TopRepo{ - RepoName: repo.Name, - AccessCount: repo.PullCount, - }) - } - - ra.Data["json"] = result - ra.ServeJSON() - return - } - result, err := populateTagsCount(repos) if err != nil { log.Errorf("failed to popultate tags count to repositories: %v", err) @@ -579,15 +484,10 @@ func (ra *RepositoryAPI) GetTopRepos() { //GetSignatures returns signatures of a repository func (ra *RepositoryAPI) GetSignatures() { - //use this func to init session. - ra.GetUserIDForRequest() - username, err := ra.getUsername() - if err != nil { - log.Warningf("Error when getting username: %v", err) - } repoName := ra.GetString(":splat") - targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(), username, repoName) + targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(), + ra.SecurityCxt.GetUsername(), repoName) if err != nil { log.Errorf("Error while fetching signature from notary: %v", err) ra.CustomAbort(http.StatusInternalServerError, "internal error") @@ -595,23 +495,3 @@ func (ra *RepositoryAPI) GetSignatures() { ra.Data["json"] = targets ra.ServeJSON() } - -func newRepositoryClient(endpoint string, insecure bool, username, password, repository, scopeType, scopeName string, - scopeActions ...string) (*registry.Repository, error) { - - credential := auth.NewBasicAuthCredential(username, password) - - authorizer := auth.NewStandardTokenAuthorizer(credential, insecure, - config.InternalTokenServiceEndpoint(), scopeType, scopeName, scopeActions...) - - store, err := auth.NewAuthorizerStore(endpoint, insecure, authorizer) - if err != nil { - return nil, err - } - - client, err := registry.NewRepositoryWithModifiers(repository, endpoint, insecure, store) - if err != nil { - return nil, err - } - return client, nil -} diff --git a/src/ui/api/utils.go b/src/ui/api/utils.go index 62d7cbb2c..e90757cb3 100644 --- a/src/ui/api/utils.go +++ b/src/ui/api/utils.go @@ -42,6 +42,7 @@ func checkProjectPermission(userID int, projectID int64) bool { return len(roles) > 0 } +// TODO remove func hasProjectAdminRole(userID int, projectID int64) bool { roles, err := listRoles(userID, projectID) if err != nil { diff --git a/src/ui/projectmanager/db/pm.go b/src/ui/projectmanager/db/pm.go index e2cf75da0..c191099c8 100644 --- a/src/ui/projectmanager/db/pm.go +++ b/src/ui/projectmanager/db/pm.go @@ -24,27 +24,39 @@ import ( // ProjectManager implements pm.PM interface based on database type ProjectManager struct{} -// IsPublic returns whether the project is public or not -func (p *ProjectManager) IsPublic(projectIDOrName interface{}) bool { - var project *models.Project - var err error +// Get ... +func (p *ProjectManager) Get(projectIDOrName interface{}) *models.Project { switch projectIDOrName.(type) { case string: name := projectIDOrName.(string) - project, err = dao.GetProjectByName(name) + project, err := dao.GetProjectByName(name) if err != nil { log.Errorf("failed to get project %s: %v", name, err) + return nil } + return project case int64: id := projectIDOrName.(int64) - project, err = dao.GetProjectByID(id) + project, err := dao.GetProjectByID(id) if err != nil { log.Errorf("failed to get project %d: %v", id, err) + return nil } + return project default: log.Errorf("unsupported type of %v, must be string or int64", projectIDOrName) + return nil } +} +// Exist ... +func (p *ProjectManager) Exist(projectIDOrName interface{}) bool { + return p.Get(projectIDOrName) != 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 } @@ -67,31 +79,15 @@ func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) return roles } - var projectID int64 - 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 roles - } - - if project == nil { - return roles - } - projectID = project.ProjectID - case int64: - projectID = projectIDOrName.(int64) - default: - log.Errorf("unsupported type of %v, must be string or int64", projectIDOrName) + project := p.Get(projectIDOrName) + if project == nil { return roles } - roleList, err := dao.GetUserProjectRoles(user.UserID, projectID) + 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, projectID, err) + user.UserID, project.ProjectID, err) return roles } @@ -108,3 +104,25 @@ func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) return roles } + +// GetPublic returns all public projects +func (p *ProjectManager) GetPublic() []models.Project { + projects, err := dao.GetProjects("", 1) + if err != nil { + log.Errorf("failed to get all public projects: %v", err) + return []models.Project{} + } + + return projects +} + +// GetByMember returns all projects which the user is a member of +func (p *ProjectManager) GetByMember(username string) []models.Project { + projects, err := dao.GetProjects(username) + if err != nil { + log.Errorf("failed to get projects of %s: %v", username, err) + return []models.Project{} + } + + return projects +} diff --git a/src/ui/projectmanager/db/pm_test.go b/src/ui/projectmanager/db/pm_test.go index f1fc7e5f9..7bce75435 100644 --- a/src/ui/projectmanager/db/pm_test.go +++ b/src/ui/projectmanager/db/pm_test.go @@ -70,16 +70,44 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +func TestGet(t *testing.T) { + pm := &ProjectManager{} + + // project name + project := pm.Get("library") + assert.NotNil(t, project) + assert.Equal(t, "library", project.Name) + + // project ID + project = pm.Get(int64(1)) + assert.NotNil(t, project) + assert.Equal(t, int64(1), project.ProjectID) + + // non-exist project + project = pm.Get("non-exist-project") + assert.Nil(t, project) + + // invalid type + project = pm.Get(true) + assert.Nil(t, project) +} + +func TestExist(t *testing.T) { + pm := &ProjectManager{} + + // exist project + assert.True(t, pm.Exist("library")) + + // non-exist project + assert.False(t, pm.Exist("non-exist-project")) +} + func TestIsPublic(t *testing.T) { pms := &ProjectManager{} - // project name + // public project assert.True(t, pms.IsPublic("library")) - // project ID - assert.True(t, pms.IsPublic(int64(1))) // non exist project assert.False(t, pms.IsPublic("non_exist_project")) - // invalid type - assert.False(t, pms.IsPublic(1)) } func TestGetRoles(t *testing.T) { @@ -89,18 +117,28 @@ func TestGetRoles(t *testing.T) { assert.Equal(t, []int{}, pm.GetRoles("non_exist_user", int64(1))) - // project ID - assert.Equal(t, []int{common.RoleProjectAdmin}, - pm.GetRoles("admin", int64(1))) - - // project name + // exist project assert.Equal(t, []int{common.RoleProjectAdmin}, pm.GetRoles("admin", "library")) - // non exist project + // non-exist project assert.Equal(t, []int{}, pm.GetRoles("admin", "non_exist_project")) - - // invalid type - assert.Equal(t, []int{}, pm.GetRoles("admin", 1)) +} + +func TestGetPublic(t *testing.T) { + pm := &ProjectManager{} + projects := pm.GetPublic() + + assert.NotEqual(t, 0, len(projects)) + + for _, project := range projects { + assert.Equal(t, 1, project.Public) + } +} + +func TestGetByMember(t *testing.T) { + pm := &ProjectManager{} + projects := pm.GetByMember("admin") + assert.NotEqual(t, 0, len(projects)) } diff --git a/src/ui/projectmanager/pm.go b/src/ui/projectmanager/pm.go index 3fae44fba..8f3763c8b 100644 --- a/src/ui/projectmanager/pm.go +++ b/src/ui/projectmanager/pm.go @@ -14,9 +14,19 @@ package projectmanager +import ( + "github.com/vmware/harbor/src/common/models" +) + // 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 all public project + GetPublic() []models.Project + // get projects which the user is a member of + GetByMember(username string) []models.Project }