Change to repositories api

Update to repositories API so it will not differentiate if a project
does not exist or the user doesn't have permission to access it.

Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
Daniel Jiang 2020-05-22 18:29:51 +08:00
parent d6cab4fb8e
commit d8336c4549
7 changed files with 53 additions and 83 deletions

View File

@ -102,24 +102,19 @@ func (b *BaseController) RequireAuthenticated() bool {
// HasProjectPermission returns true when the request has action permission on project subresource // HasProjectPermission returns true when the request has action permission on project subresource
func (b *BaseController) HasProjectPermission(projectIDOrName interface{}, action rbac.Action, subresource ...rbac.Resource) (bool, error) { func (b *BaseController) HasProjectPermission(projectIDOrName interface{}, action rbac.Action, subresource ...rbac.Resource) (bool, error) {
projectID, projectName, err := utils.ParseProjectIDOrName(projectIDOrName) _, _, err := utils.ParseProjectIDOrName(projectIDOrName)
if err != nil { if err != nil {
return false, err return false, err
} }
project, err := b.ProjectMgr.Get(projectIDOrName)
if projectName != "" { if err != nil {
project, err := b.ProjectMgr.Get(projectName) return false, err
if err != nil { }
return false, err if project == nil {
} return false, errNotFound
if project == nil {
return false, errNotFound
}
projectID = project.ProjectID
} }
resource := rbac.NewProjectNamespace(projectID).Resource(subresource...) resource := rbac.NewProjectNamespace(project.ProjectID).Resource(subresource...)
if !b.SecurityCtx.Can(action, resource) { if !b.SecurityCtx.Can(action, resource) {
return false, nil return false, nil
} }
@ -133,27 +128,39 @@ func (b *BaseController) RequireProjectAccess(projectIDOrName interface{}, actio
hasPermission, err := b.HasProjectPermission(projectIDOrName, action, subresource...) hasPermission, err := b.HasProjectPermission(projectIDOrName, action, subresource...)
if err != nil { if err != nil {
if err == errNotFound { if err == errNotFound {
b.SendNotFoundError(fmt.Errorf("project %v not found", projectIDOrName)) b.handleProjectNotFound(projectIDOrName)
} else { } else {
b.SendInternalServerError(err) b.SendInternalServerError(err)
} }
return false return false
} }
if !hasPermission { if !hasPermission {
if !b.SecurityCtx.IsAuthenticated() { b.SendPermissionError()
b.SendUnAuthorizedError(errors.New("UnAuthorized"))
} else {
b.SendForbiddenError(errors.New(b.SecurityCtx.GetUsername()))
}
return false return false
} }
return true return true
} }
// This should be called when a project is not found, if the caller is a system admin it returns 404.
// If it's regular user, it will render permission error
func (b *BaseController) handleProjectNotFound(projectIDOrName interface{}) {
if b.SecurityCtx.IsSysAdmin() {
b.SendNotFoundError(fmt.Errorf("project %v not found", projectIDOrName))
} else {
b.SendPermissionError()
}
}
// SendPermissionError is a shortcut for sending different http error based on authentication status.
func (b *BaseController) SendPermissionError() {
if !b.SecurityCtx.IsAuthenticated() {
b.SendUnAuthorizedError(errors.New("UnAuthorized"))
} else {
b.SendForbiddenError(errors.New(b.SecurityCtx.GetUsername()))
}
}
// WriteJSONData writes the JSON data to the client. // WriteJSONData writes the JSON data to the client.
func (b *BaseController) WriteJSONData(object interface{}) { func (b *BaseController) WriteJSONData(object interface{}) {
b.Data["json"] = object b.Data["json"] = object

View File

@ -69,7 +69,7 @@ func (m *MetadataAPI) Prepare() {
} }
if project == nil { if project == nil {
m.SendNotFoundError(fmt.Errorf("project %d not found", id)) m.handleProjectNotFound(id)
return return
} }

View File

@ -65,10 +65,10 @@ func TestValidateProjectMetadata(t *testing.T) {
func TestMetaAPI(t *testing.T) { func TestMetaAPI(t *testing.T) {
client := newHarborAPI() client := newHarborAPI()
// non-exist project // non-exist project, it should return 401 if user is not logged in.
code, _, err := client.PostMeta(*unknownUsr, int64(1000), nil) code, _, err := client.PostMeta(*unknownUsr, int64(1000), nil)
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, http.StatusNotFound, code) assert.Equal(t, http.StatusUnauthorized, code)
// non-login // non-login
code, _, err = client.PostMeta(*unknownUsr, int64(1), nil) code, _, err = client.PostMeta(*unknownUsr, int64(1), nil)

View File

@ -76,7 +76,7 @@ func (p *ProjectAPI) Prepare() {
} }
if project == nil { if project == nil {
p.SendNotFoundError(fmt.Errorf("project %d not found", id)) p.handleProjectNotFound(id)
return return
} }

View File

@ -103,18 +103,6 @@ func (ra *RepositoryAPI) Get() {
return return
} }
exist, err := ra.ProjectMgr.Exists(projectID)
if err != nil {
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %d",
projectID), err)
return
}
if !exist {
ra.SendNotFoundError(fmt.Errorf("project %d not found", projectID))
return
}
if !ra.RequireProjectAccess(projectID, rbac.ActionList, rbac.ResourceRepository) { if !ra.RequireProjectAccess(projectID, rbac.ActionList, rbac.ResourceRepository) {
return return
} }
@ -411,6 +399,10 @@ func (ra *RepositoryAPI) Delete() {
func (ra *RepositoryAPI) GetTag() { func (ra *RepositoryAPI) GetTag() {
repository := ra.GetString(":splat") repository := ra.GetString(":splat")
tag := ra.GetString(":tag") tag := ra.GetString(":tag")
projectName, _ := utils.ParseRepository(repository)
if !ra.RequireProjectAccess(projectName, rbac.ActionRead, rbac.ResourceRepositoryTag) {
return
}
exist, _, err := ra.checkExistence(repository, tag) exist, _, err := ra.checkExistence(repository, tag)
if err != nil { if err != nil {
ra.SendInternalServerError(fmt.Errorf("failed to check the existence of resource, error: %v", err)) ra.SendInternalServerError(fmt.Errorf("failed to check the existence of resource, error: %v", err))
@ -421,11 +413,6 @@ func (ra *RepositoryAPI) GetTag() {
return return
} }
projectName, _ := utils.ParseRepository(repository)
if !ra.RequireProjectAccess(projectName, rbac.ActionRead, rbac.ResourceRepositoryTag) {
return
}
project, err := ra.ProjectMgr.Get(projectName) project, err := ra.ProjectMgr.Get(projectName)
if err != nil { if err != nil {
ra.ParseAndHandleError(fmt.Sprintf("failed to get the project %s", ra.ParseAndHandleError(fmt.Sprintf("failed to get the project %s",
@ -559,6 +546,9 @@ func (ra *RepositoryAPI) GetTags() {
} }
projectName, _ := utils.ParseRepository(repoName) projectName, _ := utils.ParseRepository(repoName)
if !ra.RequireProjectAccess(projectName, rbac.ActionList, rbac.ResourceRepositoryTag) {
return
}
project, err := ra.ProjectMgr.Get(projectName) project, err := ra.ProjectMgr.Get(projectName)
if err != nil { if err != nil {
ra.ParseAndHandleError(fmt.Sprintf("failed to get the project %s", ra.ParseAndHandleError(fmt.Sprintf("failed to get the project %s",
@ -571,10 +561,6 @@ func (ra *RepositoryAPI) GetTags() {
return return
} }
if !ra.RequireProjectAccess(projectName, rbac.ActionList, rbac.ResourceRepositoryTag) {
return
}
client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName) client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {
log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
@ -853,18 +839,6 @@ func (ra *RepositoryAPI) GetManifests() {
} }
projectName, _ := utils.ParseRepository(repoName) projectName, _ := utils.ParseRepository(repoName)
exist, err := ra.ProjectMgr.Exists(projectName)
if err != nil {
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s",
projectName), err)
return
}
if !exist {
ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return
}
if !ra.RequireProjectAccess(projectName, rbac.ActionRead, rbac.ResourceRepositoryTagManifest) { if !ra.RequireProjectAccess(projectName, rbac.ActionRead, rbac.ResourceRepositoryTagManifest) {
return return
} }
@ -1011,18 +985,6 @@ func (ra *RepositoryAPI) GetSignatures() {
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
projectName, _ := utils.ParseRepository(repoName) projectName, _ := utils.ParseRepository(repoName)
exist, err := ra.ProjectMgr.Exists(projectName)
if err != nil {
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s",
projectName), err)
return
}
if !exist {
ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return
}
if !ra.RequireProjectAccess(projectName, rbac.ActionRead, rbac.ResourceRepository) { if !ra.RequireProjectAccess(projectName, rbac.ActionRead, rbac.ResourceRepository) {
return return
} }

View File

@ -48,11 +48,11 @@ func TestGetRepos(t *testing.T) {
} }
// -------------------case 2 : response code = 404------------------------// // -------------------case 2 : response code = 404------------------------//
fmt.Println("case 2 : response code = 404:project not found") fmt.Println("case 2 : response code = 404: project not found")
projectID = "111" projectID = "111"
httpStatusCode, _, err := apiTest.GetRepos(*admin, projectID, keyword) httpStatusCode, _, err := apiTest.GetRepos(*admin, projectID, keyword)
if err != nil { if err != nil {
t.Error("Error whihle get repos by projectID", err.Error()) t.Error("Error while get repos by projectID", err.Error())
t.Log(err) t.Log(err)
} else { } else {
assert.Equal(int(404), httpStatusCode, "httpStatusCode should be 404") assert.Equal(int(404), httpStatusCode, "httpStatusCode should be 404")
@ -77,8 +77,8 @@ func TestGetReposTags(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
apiTest := newHarborAPI() apiTest := newHarborAPI()
// -------------------case 1 : response code = 404------------------------// // -------------------case 1 : response code = 404---------------------//
fmt.Println("case 1 : response code = 404,repo not found") fmt.Println("case 1 : response code = 404")
repository := "errorRepos" repository := "errorRepos"
code, _, err := apiTest.GetReposTags(*admin, repository) code, _, err := apiTest.GetReposTags(*admin, repository)
if err != nil { if err != nil {
@ -155,7 +155,7 @@ func TestGetReposManifests(t *testing.T) {
} }
// -------------------case 3 : response code = 404------------------------// // -------------------case 3 : response code = 404------------------------//
fmt.Println("case 3 : response code = 404,repo not found") fmt.Println("case 3 : response code = 404")
repoName = "111" repoName = "111"
httpStatusCode, err = apiTest.GetReposManifests(*admin, repoName, tag) httpStatusCode, err = apiTest.GetReposManifests(*admin, repoName, tag)
if err != nil { if err != nil {

View File

@ -21,6 +21,7 @@ import (
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/log"
coreutils "github.com/goharbor/harbor/src/core/utils" coreutils "github.com/goharbor/harbor/src/core/utils"
"github.com/goharbor/harbor/src/jobservice/logger" "github.com/goharbor/harbor/src/jobservice/logger"
"github.com/goharbor/harbor/src/pkg/scan/api/scan" "github.com/goharbor/harbor/src/pkg/scan/api/scan"
@ -47,6 +48,10 @@ func (sa *ScanAPI) Prepare() {
// Call super prepare method // Call super prepare method
sa.BaseController.Prepare() sa.BaseController.Prepare()
// Check authentication
if !sa.RequireAuthenticated() {
return
}
// Parse parameters // Parse parameters
repoName := sa.GetString(":splat") repoName := sa.GetString(":splat")
tag := sa.GetString(":tag") tag := sa.GetString(":tag")
@ -58,16 +63,12 @@ func (sa *ScanAPI) Prepare() {
return return
} }
if pro == nil { if pro == nil {
sa.SendNotFoundError(errors.Errorf("project %s not found", projectName)) log.Errorf("project %s not found", projectName)
sa.SendForbiddenError(errors.New(sa.SecurityCtx.GetUsername()))
return return
} }
sa.pro = pro sa.pro = pro
// Check authentication
if !sa.RequireAuthenticated() {
return
}
// Assemble artifact object // Assemble artifact object
digest, err := digestFunc(repoName, tag, sa.SecurityCtx.GetUsername()) digest, err := digestFunc(repoName, tag, sa.SecurityCtx.GetUsername())
if err != nil { if err != nil {