Merge pull request #10643 from ywk253100/200204_auth

Add permission check for artifact related APIs
This commit is contained in:
Wang Yan 2020-02-07 11:25:37 +08:00 committed by GitHub
commit 5679c174c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 6 deletions

View File

@ -15,8 +15,6 @@ consumes:
securityDefinitions: securityDefinitions:
basicAuth: basicAuth:
type: basic type: basic
security:
- basicAuth: []
paths: paths:
/projects/{project_name}/repositories/{repository_name}/artifacts: /projects/{project_name}/repositories/{repository_name}/artifacts:
get: get:

View File

@ -53,7 +53,7 @@ const (
ResourceTagRetention = Resource("tag-retention") ResourceTagRetention = Resource("tag-retention")
ResourceImmutableTag = Resource("immutable-tag") ResourceImmutableTag = Resource("immutable-tag")
ResourceRepositoryLabel = Resource("repository-label") ResourceRepositoryLabel = Resource("repository-label")
ResourceRepositoryTag = Resource("repository-tag") ResourceRepositoryTag = Resource("repository-tag") // TODO remove
ResourceRepositoryTagLabel = Resource("repository-tag-label") ResourceRepositoryTagLabel = Resource("repository-tag-label")
ResourceRepositoryTagManifest = Resource("repository-tag-manifest") ResourceRepositoryTagManifest = Resource("repository-tag-manifest")
ResourceRepositoryTagScanJob = Resource("repository-tag-scan-job") // TODO: remove ResourceRepositoryTagScanJob = Resource("repository-tag-scan-job") // TODO: remove
@ -62,5 +62,7 @@ const (
ResourceNotificationPolicy = Resource("notification-policy") ResourceNotificationPolicy = Resource("notification-policy")
ResourceScan = Resource("scan") ResourceScan = Resource("scan")
ResourceScanner = Resource("scanner") ResourceScanner = Resource("scanner")
ResourceArtifact = Resource("artifact")
ResourceTag = Resource("tag")
ResourceSelf = Resource("") // subresource for self ResourceSelf = Resource("") // subresource for self
) )

View File

@ -48,6 +48,9 @@ var (
{Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead},
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
} }
// all policies for the projects // all policies for the projects

View File

@ -127,6 +127,13 @@ var (
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
{Resource: rbac.ResourceScanner, Action: rbac.ActionCreate}, {Resource: rbac.ResourceScanner, Action: rbac.ActionCreate},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionDelete},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
{Resource: rbac.ResourceTag, Action: rbac.ActionCreate},
{Resource: rbac.ResourceTag, Action: rbac.ActionDelete},
}, },
"master": { "master": {
@ -216,6 +223,13 @@ var (
{Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead},
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionDelete},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
{Resource: rbac.ResourceTag, Action: rbac.ActionCreate},
{Resource: rbac.ResourceTag, Action: rbac.ActionDelete},
}, },
"developer": { "developer": {
@ -272,6 +286,11 @@ var (
{Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead},
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
{Resource: rbac.ResourceTag, Action: rbac.ActionCreate},
}, },
"guest": { "guest": {
@ -316,6 +335,9 @@ var (
{Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead},
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
}, },
"limitedGuest": { "limitedGuest": {
@ -344,6 +366,9 @@ var (
{Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead},
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
}, },
} }
) )

View File

@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"github.com/go-openapi/runtime/middleware" "github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/api/artifact" "github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/common/rbac"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/project" "github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/pkg/q" "github.com/goharbor/harbor/src/pkg/q"
@ -43,9 +44,10 @@ type artifactAPI struct {
repoMgr repository.Manager repoMgr repository.Manager
} }
// TODO do auth in a separate middleware
func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListArtifactsParams) middleware.Responder { func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListArtifactsParams) middleware.Responder {
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionList, rbac.ResourceArtifact); err != nil {
return a.SendError(ctx, err)
}
// set query // set query
query := &q.Query{ query := &q.Query{
Keywords: map[string]interface{}{}, Keywords: map[string]interface{}{},
@ -85,6 +87,9 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
} }
func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtifactParams) middleware.Responder { func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtifactParams) middleware.Responder {
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionRead, rbac.ResourceArtifact); err != nil {
return a.SendError(ctx, err)
}
// set option // set option
option := option(params.WithTag, params.WithImmutableStatus, option := option(params.WithTag, params.WithImmutableStatus,
params.WithLabel, params.WithScanOverview, params.WithSignature) params.WithLabel, params.WithScanOverview, params.WithSignature)
@ -98,6 +103,9 @@ func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtif
} }
func (a *artifactAPI) DeleteArtifact(ctx context.Context, params operation.DeleteArtifactParams) middleware.Responder { func (a *artifactAPI) DeleteArtifact(ctx context.Context, params operation.DeleteArtifactParams) middleware.Responder {
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionDelete, rbac.ResourceArtifact); err != nil {
return a.SendError(ctx, err)
}
artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), params.Reference, nil) artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), params.Reference, nil)
if err != nil { if err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
@ -109,6 +117,9 @@ func (a *artifactAPI) DeleteArtifact(ctx context.Context, params operation.Delet
} }
func (a *artifactAPI) CreateTag(ctx context.Context, params operation.CreateTagParams) middleware.Responder { func (a *artifactAPI) CreateTag(ctx context.Context, params operation.CreateTagParams) middleware.Responder {
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionCreate, rbac.ResourceTag); err != nil {
return a.SendError(ctx, err)
}
art, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), art, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName),
params.Reference, &artifact.Option{ params.Reference, &artifact.Option{
WithTag: true, WithTag: true,
@ -129,6 +140,9 @@ func (a *artifactAPI) CreateTag(ctx context.Context, params operation.CreateTagP
} }
func (a *artifactAPI) DeleteTag(ctx context.Context, params operation.DeleteTagParams) middleware.Responder { func (a *artifactAPI) DeleteTag(ctx context.Context, params operation.DeleteTagParams) middleware.Responder {
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionDelete, rbac.ResourceTag); err != nil {
return a.SendError(ctx, err)
}
artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName),
params.Reference, &artifact.Option{ params.Reference, &artifact.Option{
WithTag: true, WithTag: true,

View File

@ -18,6 +18,8 @@ package handler
import ( import (
"context" "context"
"errors"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/go-openapi/runtime/middleware" "github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/rbac"
@ -45,7 +47,7 @@ func (*BaseAPI) SendError(ctx context.Context, err error) middleware.Responder {
func (*BaseAPI) HasPermission(ctx context.Context, action rbac.Action, resource rbac.Resource) bool { func (*BaseAPI) HasPermission(ctx context.Context, action rbac.Action, resource rbac.Resource) bool {
s, ok := security.FromContext(ctx) s, ok := security.FromContext(ctx)
if !ok { if !ok {
log.Warningf("security not found in the contxt") log.Warningf("security not found in the context")
return false return false
} }
@ -77,3 +79,19 @@ func (b *BaseAPI) HasProjectPermission(ctx context.Context, projectIDOrName inte
resource := rbac.NewProjectNamespace(projectID).Resource(subresource...) resource := rbac.NewProjectNamespace(projectID).Resource(subresource...)
return b.HasPermission(ctx, action, resource) return b.HasPermission(ctx, action, resource)
} }
// RequireProjectAccess checks the permission against the resources according to the context
// An error will be returned if it doesn't meet the requirement
func (b *BaseAPI) RequireProjectAccess(ctx context.Context, projectIDOrName interface{}, action rbac.Action, subresource ...rbac.Resource) error {
if b.HasProjectPermission(ctx, projectIDOrName, action, subresource...) {
return nil
}
secCtx, ok := security.FromContext(ctx)
if !ok {
return ierror.UnauthorizedError(errors.New("security context not found"))
}
if !secCtx.IsAuthenticated() {
return ierror.UnauthorizedError(nil)
}
return ierror.ForbiddenError(nil)
}