refactor repository API

This commit is contained in:
Wenkai Yin 2017-05-09 18:18:23 +08:00
parent b5ef2c231d
commit b02bbc0adf
9 changed files with 274 additions and 263 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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"`
}

54
src/ui/api/base.go Normal file
View File

@ -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, "")
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}