Merge pull request #6852 from heww/protect-api-using-rbac

Protect API using rbac
This commit is contained in:
He Weiwei 2019-02-01 21:11:26 +08:00 committed by GitHub
commit f7218f80e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 493 additions and 407 deletions

View File

@ -38,11 +38,14 @@ const (
ResourceHelmChartVersion = Resource("helm-chart-version") ResourceHelmChartVersion = Resource("helm-chart-version")
ResourceHelmChartVersionLabel = Resource("helm-chart-version-label") ResourceHelmChartVersionLabel = Resource("helm-chart-version-label")
ResourceLabel = Resource("label") ResourceLabel = Resource("label")
ResourceLabelResource = Resource("label-resource")
ResourceLog = Resource("log") ResourceLog = Resource("log")
ResourceMember = Resource("member") ResourceMember = Resource("member")
ResourceMetadata = Resource("metadata")
ResourceReplication = Resource("replication") ResourceReplication = Resource("replication")
ResourceReplicationJob = Resource("replication-job") ResourceReplicationJob = Resource("replication-job")
ResourceRepository = Resource("repository") ResourceRepository = Resource("repository")
ResourceRepositoryLabel = Resource("repository-label")
ResourceRepositoryTag = Resource("repository-tag") ResourceRepositoryTag = Resource("repository-tag")
ResourceRepositoryTagLabel = Resource("repository-tag-label") ResourceRepositoryTagLabel = Resource("repository-tag-label")
ResourceRepositoryTagManifest = Resource("repository-tag-manifest") ResourceRepositoryTagManifest = Resource("repository-tag-manifest")

View File

@ -52,6 +52,10 @@ func (ns *projectNamespace) IsPublic() bool {
} }
// NewProjectNamespace returns namespace for project // NewProjectNamespace returns namespace for project
func NewProjectNamespace(projectIDOrName interface{}, isPublic bool) Namespace { func NewProjectNamespace(projectIDOrName interface{}, isPublic ...bool) Namespace {
return &projectNamespace{projectIDOrName: projectIDOrName, isPublic: isPublic} isPublicNamespace := false
if len(isPublic) > 0 {
isPublicNamespace = isPublic[0]
}
return &projectNamespace{projectIDOrName: projectIDOrName, isPublic: isPublicNamespace}
} }

View File

@ -23,9 +23,23 @@ var (
publicProjectPolicies = []*rbac.Policy{ publicProjectPolicies = []*rbac.Policy{
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead}, {Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionList}, {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionList}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
@ -44,10 +58,16 @@ var (
{Resource: rbac.ResourceMember, Action: rbac.ActionDelete}, {Resource: rbac.ResourceMember, Action: rbac.ActionDelete},
{Resource: rbac.ResourceMember, Action: rbac.ActionList}, {Resource: rbac.ResourceMember, Action: rbac.ActionList},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionCreate},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionRead},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionDelete},
{Resource: rbac.ResourceLog, Action: rbac.ActionList}, {Resource: rbac.ResourceLog, Action: rbac.ActionList},
{Resource: rbac.ResourceReplication, Action: rbac.ActionList}, {Resource: rbac.ResourceReplication, Action: rbac.ActionList},
{Resource: rbac.ResourceReplication, Action: rbac.ActionCreate}, {Resource: rbac.ResourceReplication, Action: rbac.ActionCreate},
{Resource: rbac.ResourceReplication, Action: rbac.ActionRead},
{Resource: rbac.ResourceReplication, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceReplication, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceReplication, Action: rbac.ActionDelete}, {Resource: rbac.ResourceReplication, Action: rbac.ActionDelete},
@ -56,17 +76,25 @@ var (
{Resource: rbac.ResourceReplicationJob, Action: rbac.ActionList}, {Resource: rbac.ResourceReplicationJob, Action: rbac.ActionList},
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceLabel, Action: rbac.ActionList}, {Resource: rbac.ResourceLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceLabelResource, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepository, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepository, Action: rbac.ActionList}, {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPushPull}, // compatible with security all perm of project
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPushPull}, // compatible with security all perm of project
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete},
@ -81,12 +109,14 @@ var (
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionDelete}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionDelete},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionList}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionCreate},
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead}, {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionDelete}, {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionDelete},
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList}, {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},

View File

@ -31,6 +31,11 @@ var (
{Resource: rbac.ResourceMember, Action: rbac.ActionDelete}, {Resource: rbac.ResourceMember, Action: rbac.ActionDelete},
{Resource: rbac.ResourceMember, Action: rbac.ActionList}, {Resource: rbac.ResourceMember, Action: rbac.ActionList},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionCreate},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionRead},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionDelete},
{Resource: rbac.ResourceLog, Action: rbac.ActionList}, {Resource: rbac.ResourceLog, Action: rbac.ActionList},
{Resource: rbac.ResourceReplication, Action: rbac.ActionRead}, {Resource: rbac.ResourceReplication, Action: rbac.ActionRead},
@ -40,18 +45,25 @@ var (
{Resource: rbac.ResourceReplicationJob, Action: rbac.ActionList}, {Resource: rbac.ResourceReplicationJob, Action: rbac.ActionList},
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceLabel, Action: rbac.ActionList}, {Resource: rbac.ResourceLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceLabelResource, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepository, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepository, Action: rbac.ActionList}, {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPushPull}, // compatible with security all perm of project
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPushPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPushPull}, // compatible with security all perm of project
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete},
@ -66,6 +78,7 @@ var (
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate}, // upload helm chart {Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate}, // upload helm chart
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead}, // download helm chart {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead}, // download helm chart
@ -95,23 +108,34 @@ var (
{Resource: rbac.ResourceMember, Action: rbac.ActionList}, {Resource: rbac.ResourceMember, Action: rbac.ActionList},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionCreate},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionRead},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceMetadata, Action: rbac.ActionDelete},
{Resource: rbac.ResourceLog, Action: rbac.ActionList}, {Resource: rbac.ResourceLog, Action: rbac.ActionList},
{Resource: rbac.ResourceReplication, Action: rbac.ActionRead}, {Resource: rbac.ResourceReplication, Action: rbac.ActionRead},
{Resource: rbac.ResourceReplication, Action: rbac.ActionList}, {Resource: rbac.ResourceReplication, Action: rbac.ActionList},
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceLabel, Action: rbac.ActionList}, {Resource: rbac.ResourceLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepository, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepository, Action: rbac.ActionList}, {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
@ -125,6 +149,7 @@ var (
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
@ -140,7 +165,9 @@ var (
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead}, {Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
}, },
"developer": { "developer": {
@ -150,11 +177,20 @@ var (
{Resource: rbac.ResourceLog, Action: rbac.ActionList}, {Resource: rbac.ResourceLog, Action: rbac.ActionList},
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
{Resource: rbac.ResourceRepository, Action: rbac.ActionList}, {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
@ -164,6 +200,7 @@ var (
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate},
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead}, {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
@ -177,6 +214,9 @@ var (
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete}, {Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete},
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead}, {Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
}, },
"guest": { "guest": {
@ -186,12 +226,20 @@ var (
{Resource: rbac.ResourceLog, Action: rbac.ActionList}, {Resource: rbac.ResourceLog, Action: rbac.ActionList},
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepository, Action: rbac.ActionList}, {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull}, {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList}, {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList}, {Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
{Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead}, {Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
@ -203,6 +251,9 @@ var (
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList}, {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead}, {Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
}, },
} }
) )

View File

@ -69,24 +69,6 @@ func (s *SecurityContext) IsSolutionUser() bool {
return false return false
} }
// HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPull, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// HasWritePerm returns whether the user has write permission to the project
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPush, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// HasAllPerm returns whether the user has all permissions to the project
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPushPull, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// Can returns whether the user can do action on resource // Can returns whether the user can do action on resource
func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool { func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
ns, err := resource.GetNamespace() ns, err := resource.GetNamespace()

View File

@ -29,12 +29,6 @@ type Context interface {
IsSysAdmin() bool IsSysAdmin() bool
// IsSolutionUser returns whether the user is solution user // IsSolutionUser returns whether the user is solution user
IsSolutionUser() bool IsSolutionUser() bool
// HasReadPerm returns whether the user has read permission to the project
HasReadPerm(projectIDOrName interface{}) bool
// HasWritePerm returns whether the user has write permission to the project
HasWritePerm(projectIDOrName interface{}) bool
// HasAllPerm returns whether the user has all permissions to the project
HasAllPerm(projectIDOrName interface{}) bool
// Get current user's all project // Get current user's all project
GetMyProjects() ([]*models.Project, error) GetMyProjects() ([]*models.Project, error)
// Get user's role in provided project // Get user's role in provided project

View File

@ -67,24 +67,6 @@ func (s *SecurityContext) IsSolutionUser() bool {
return false return false
} }
// HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPull, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// HasWritePerm returns whether the user has write permission to the project
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPush, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// HasAllPerm returns whether the user has all permissions to the project
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPushPull, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// Can returns whether the user can do action on resource // Can returns whether the user can do action on resource
func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool { func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
ns, err := resource.GetNamespace() ns, err := resource.GetNamespace()

View File

@ -23,6 +23,7 @@ import (
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/dao/project" "github.com/goharbor/harbor/src/common/dao/project"
"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/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/promgr" "github.com/goharbor/harbor/src/core/promgr"
"github.com/goharbor/harbor/src/core/promgr/pmsdriver/local" "github.com/goharbor/harbor/src/core/promgr/pmsdriver/local"
@ -210,66 +211,73 @@ func TestIsSolutionUser(t *testing.T) {
func TestHasReadPerm(t *testing.T) { func TestHasReadPerm(t *testing.T) {
// public project // public project
ctx := NewSecurityContext(nil, pm) ctx := NewSecurityContext(nil, pm)
assert.True(t, ctx.HasReadPerm("library"))
resource := rbac.NewProjectNamespace("library").Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPull, resource))
// private project, unauthenticated // private project, unauthenticated
ctx = NewSecurityContext(nil, pm) ctx = NewSecurityContext(nil, pm)
assert.False(t, ctx.HasReadPerm(private.Name)) resource = rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
assert.False(t, ctx.Can(rbac.ActionPull, resource))
// private project, authenticated, has no perm // private project, authenticated, has no perm
ctx = NewSecurityContext(&models.User{ ctx = NewSecurityContext(&models.User{
Username: "test", Username: "test",
}, pm) }, pm)
assert.False(t, ctx.HasReadPerm(private.Name)) assert.False(t, ctx.Can(rbac.ActionPull, resource))
// private project, authenticated, has read perm // private project, authenticated, has read perm
ctx = NewSecurityContext(guestUser, pm) ctx = NewSecurityContext(guestUser, pm)
assert.True(t, ctx.HasReadPerm(private.Name)) assert.True(t, ctx.Can(rbac.ActionPull, resource))
// private project, authenticated, system admin // private project, authenticated, system admin
ctx = NewSecurityContext(&models.User{ ctx = NewSecurityContext(&models.User{
Username: "admin", Username: "admin",
HasAdminRole: true, HasAdminRole: true,
}, pm) }, pm)
assert.True(t, ctx.HasReadPerm(private.Name)) assert.True(t, ctx.Can(rbac.ActionPull, resource))
} }
func TestHasWritePerm(t *testing.T) { func TestHasWritePerm(t *testing.T) {
resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
// unauthenticated // unauthenticated
ctx := NewSecurityContext(nil, pm) ctx := NewSecurityContext(nil, pm)
assert.False(t, ctx.HasWritePerm(private.Name)) assert.False(t, ctx.Can(rbac.ActionPush, resource))
// authenticated, has read perm // authenticated, has read perm
ctx = NewSecurityContext(guestUser, pm) ctx = NewSecurityContext(guestUser, pm)
assert.False(t, ctx.HasWritePerm(private.Name)) assert.False(t, ctx.Can(rbac.ActionPush, resource))
// authenticated, has write perm // authenticated, has write perm
ctx = NewSecurityContext(developerUser, pm) ctx = NewSecurityContext(developerUser, pm)
assert.True(t, ctx.HasWritePerm(private.Name)) assert.True(t, ctx.Can(rbac.ActionPush, resource))
// authenticated, system admin // authenticated, system admin
ctx = NewSecurityContext(&models.User{ ctx = NewSecurityContext(&models.User{
Username: "admin", Username: "admin",
HasAdminRole: true, HasAdminRole: true,
}, pm) }, pm)
assert.True(t, ctx.HasReadPerm(private.Name)) assert.True(t, ctx.Can(rbac.ActionPush, resource))
} }
func TestHasAllPerm(t *testing.T) { func TestHasAllPerm(t *testing.T) {
resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
// unauthenticated // unauthenticated
ctx := NewSecurityContext(nil, pm) ctx := NewSecurityContext(nil, pm)
assert.False(t, ctx.HasAllPerm(private.Name)) assert.False(t, ctx.Can(rbac.ActionPushPull, resource))
// authenticated, has all perms // authenticated, has all perms
ctx = NewSecurityContext(projectAdminUser, pm) ctx = NewSecurityContext(projectAdminUser, pm)
assert.True(t, ctx.HasAllPerm(private.Name)) assert.True(t, ctx.Can(rbac.ActionPushPull, resource))
// authenticated, system admin // authenticated, system admin
ctx = NewSecurityContext(&models.User{ ctx = NewSecurityContext(&models.User{
Username: "admin", Username: "admin",
HasAdminRole: true, HasAdminRole: true,
}, pm) }, pm)
assert.True(t, ctx.HasAllPerm(private.Name)) assert.True(t, ctx.Can(rbac.ActionPushPull, resource))
} }
func TestHasAllPermWithGroup(t *testing.T) { func TestHasAllPermWithGroup(t *testing.T) {
@ -285,10 +293,13 @@ func TestHasAllPermWithGroup(t *testing.T) {
developer.GroupList = []*models.UserGroup{ developer.GroupList = []*models.UserGroup{
{GroupName: "test_group", GroupType: 1, LdapGroupDN: "cn=harbor_user,dc=example,dc=com"}, {GroupName: "test_group", GroupType: 1, LdapGroupDN: "cn=harbor_user,dc=example,dc=com"},
} }
resource := rbac.NewProjectNamespace(project.Name).Resource(rbac.ResourceRepository)
ctx := NewSecurityContext(developer, pm) ctx := NewSecurityContext(developer, pm)
assert.False(t, ctx.HasAllPerm(project.Name)) assert.False(t, ctx.Can(rbac.ActionPushPull, resource))
assert.True(t, ctx.HasWritePerm(project.Name)) assert.True(t, ctx.Can(rbac.ActionPush, resource))
assert.True(t, ctx.HasReadPerm(project.Name)) assert.True(t, ctx.Can(rbac.ActionPull, resource))
} }
func TestGetMyProjects(t *testing.T) { func TestGetMyProjects(t *testing.T) {

View File

@ -60,24 +60,6 @@ func (s *SecurityContext) IsSolutionUser() bool {
return false return false
} }
// HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPull, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// HasWritePerm returns whether the user has write permission to the project
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPush, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// HasAllPerm returns whether the user has all permissions to the project
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
return s.Can(rbac.ActionPushPull, rbac.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(rbac.ResourceRepository))
}
// GetMyProjects no implementation // GetMyProjects no implementation
func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) { func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
return nil, nil return nil, nil

View File

@ -16,6 +16,7 @@ package robot
import ( import (
"os" "os"
"strconv"
"testing" "testing"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
@ -26,7 +27,6 @@ import (
"github.com/goharbor/harbor/src/core/promgr/pmsdriver/local" "github.com/goharbor/harbor/src/core/promgr/pmsdriver/local"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"strconv"
) )
var ( var (
@ -147,7 +147,8 @@ func TestHasReadPerm(t *testing.T) {
} }
ctx := NewSecurityContext(robot, pm, policies) ctx := NewSecurityContext(robot, pm, policies)
assert.True(t, ctx.HasReadPerm(private.Name)) resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPull, resource))
} }
func TestHasWritePerm(t *testing.T) { func TestHasWritePerm(t *testing.T) {
@ -164,7 +165,8 @@ func TestHasWritePerm(t *testing.T) {
} }
ctx := NewSecurityContext(robot, pm, policies) ctx := NewSecurityContext(robot, pm, policies)
assert.True(t, ctx.HasWritePerm(private.Name)) resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPush, resource))
} }
func TestHasAllPerm(t *testing.T) { func TestHasAllPerm(t *testing.T) {
@ -180,7 +182,8 @@ func TestHasAllPerm(t *testing.T) {
} }
ctx := NewSecurityContext(robot, pm, policies) ctx := NewSecurityContext(robot, pm, policies)
assert.True(t, ctx.HasAllPerm(private.Name)) resource := rbac.NewProjectNamespace(private.Name).Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPushPull, resource))
} }
func TestGetMyProjects(t *testing.T) { func TestGetMyProjects(t *testing.T) {

View File

@ -71,34 +71,9 @@ func (s *SecurityContext) IsSolutionUser() bool {
return s.IsAuthenticated() return s.IsAuthenticated()
} }
// HasReadPerm returns true if the corresponding user of the secret
// is jobservice or core service, otherwise returns false
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
if s.store == nil {
return false
}
return s.store.GetUsername(s.secret) == secret.JobserviceUser || s.store.GetUsername(s.secret) == secret.CoreUser
}
// HasWritePerm returns true if the corresponding user of the secret
// is jobservice or core service, otherwise returns false
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
if s.store == nil {
return false
}
return s.store.GetUsername(s.secret) == secret.JobserviceUser || s.store.GetUsername(s.secret) == secret.CoreUser
}
// HasAllPerm returns true if the corresponding user of the secret
// is jobservice or core service, otherwise returns false
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
if s.store == nil {
return false
}
return s.store.GetUsername(s.secret) == secret.JobserviceUser || s.store.GetUsername(s.secret) == secret.CoreUser
}
// Can returns whether the user can do action on resource // Can returns whether the user can do action on resource
// returns true if the corresponding user of the secret
// is jobservice or core service, otherwise returns false
func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool { func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
if s.store == nil { if s.store == nil {
return false return false
@ -114,7 +89,9 @@ func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
// GetProjectRoles return guest role if has read permission, otherwise return nil // GetProjectRoles return guest role if has read permission, otherwise return nil
func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int { func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
roles := []int{} roles := []int{}
if s.HasReadPerm(projectIDOrName) { if s.store != nil &&
(s.store.GetUsername(s.secret) == secret.JobserviceUser ||
s.store.GetUsername(s.secret) == secret.CoreUser) {
roles = append(roles, common.RoleGuest) roles = append(roles, common.RoleGuest)
} }
return roles return roles

View File

@ -18,6 +18,7 @@ import (
"testing" "testing"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/secret" "github.com/goharbor/harbor/src/common/secret"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -96,9 +97,11 @@ func TestIsSolutionUser(t *testing.T) {
} }
func TestHasReadPerm(t *testing.T) { func TestHasReadPerm(t *testing.T) {
readAction := rbac.Action("pull")
resource := rbac.Resource("/project/project_name/repository")
// secret store is null // secret store is null
context := NewSecurityContext("", nil) context := NewSecurityContext("", nil)
hasReadPerm := context.HasReadPerm("project_name") hasReadPerm := context.Can(readAction, resource)
assert.False(t, hasReadPerm) assert.False(t, hasReadPerm)
// invalid secret // invalid secret
@ -106,7 +109,7 @@ func TestHasReadPerm(t *testing.T) {
secret.NewStore(map[string]string{ secret.NewStore(map[string]string{
"jobservice_secret": secret.JobserviceUser, "jobservice_secret": secret.JobserviceUser,
})) }))
hasReadPerm = context.HasReadPerm("project_name") hasReadPerm = context.Can(readAction, resource)
assert.False(t, hasReadPerm) assert.False(t, hasReadPerm)
// valid secret, project name // valid secret, project name
@ -114,11 +117,12 @@ func TestHasReadPerm(t *testing.T) {
secret.NewStore(map[string]string{ secret.NewStore(map[string]string{
"jobservice_secret": secret.JobserviceUser, "jobservice_secret": secret.JobserviceUser,
})) }))
hasReadPerm = context.HasReadPerm("project_name") hasReadPerm = context.Can(readAction, resource)
assert.True(t, hasReadPerm) assert.True(t, hasReadPerm)
// valid secret, project ID // valid secret, project ID
hasReadPerm = context.HasReadPerm(1) resource = rbac.Resource("/project/1/repository")
hasReadPerm = context.Can(readAction, resource)
assert.True(t, hasReadPerm) assert.True(t, hasReadPerm)
} }
@ -128,12 +132,16 @@ func TestHasWritePerm(t *testing.T) {
"secret": "username", "secret": "username",
})) }))
writeAction := rbac.Action("push")
// project name // project name
hasWritePerm := context.HasWritePerm("project_name") resource := rbac.Resource("/project/project_name/repository")
hasWritePerm := context.Can(writeAction, resource)
assert.False(t, hasWritePerm) assert.False(t, hasWritePerm)
// project ID // project ID
hasWritePerm = context.HasWritePerm(1) resource = rbac.Resource("/project/1/repository")
hasWritePerm = context.Can(writeAction, resource)
assert.False(t, hasWritePerm) assert.False(t, hasWritePerm)
} }
@ -143,12 +151,16 @@ func TestHasAllPerm(t *testing.T) {
"secret": "username", "secret": "username",
})) }))
allAction := rbac.Action("push+pull")
// project name // project name
hasAllPerm := context.HasAllPerm("project_name") resource := rbac.Resource("/project/project_name/repository")
hasAllPerm := context.Can(allAction, resource)
assert.False(t, hasAllPerm) assert.False(t, hasAllPerm)
// project ID // project ID
hasAllPerm = context.HasAllPerm(1) resource = rbac.Resource("/project/1/repository")
hasAllPerm = context.Can(allAction, resource)
assert.False(t, hasAllPerm) assert.False(t, hasAllPerm)
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac"
) )
const ( const (
@ -45,12 +46,6 @@ func (cla *ChartLabelAPI) Prepare() {
} }
cla.project = existingProject cla.project = existingProject
// Check permission
if !cla.checkPermissions(project) {
cla.SendForbiddenError(errors.New(cla.SecurityCtx.GetUsername()))
return
}
// Check the existence of target chart // Check the existence of target chart
chartName := cla.GetStringFromPath(nameParam) chartName := cla.GetStringFromPath(nameParam)
version := cla.GetStringFromPath(versionParam) version := cla.GetStringFromPath(versionParam)
@ -62,8 +57,23 @@ func (cla *ChartLabelAPI) Prepare() {
cla.chartFullName = fmt.Sprintf("%s/%s:%s", project, chartName, version) cla.chartFullName = fmt.Sprintf("%s/%s:%s", project, chartName, version)
} }
func (cla *ChartLabelAPI) requireAccess(action rbac.Action) bool {
resource := rbac.NewProjectNamespace(cla.project.ProjectID).Resource(rbac.ResourceHelmChartVersionLabel)
if !cla.SecurityCtx.Can(action, resource) {
cla.HandleForbidden(cla.SecurityCtx.GetUsername())
return false
}
return true
}
// MarkLabel handles the request of marking label to chart. // MarkLabel handles the request of marking label to chart.
func (cla *ChartLabelAPI) MarkLabel() { func (cla *ChartLabelAPI) MarkLabel() {
if !cla.requireAccess(rbac.ActionCreate) {
return
}
l := &models.Label{} l := &models.Label{}
cla.DecodeJSONReq(l) cla.DecodeJSONReq(l)
@ -83,6 +93,10 @@ func (cla *ChartLabelAPI) MarkLabel() {
// RemoveLabel handles the request of removing label from chart. // RemoveLabel handles the request of removing label from chart.
func (cla *ChartLabelAPI) RemoveLabel() { func (cla *ChartLabelAPI) RemoveLabel() {
if !cla.requireAccess(rbac.ActionDelete) {
return
}
lID, err := cla.GetInt64FromPath(idParam) lID, err := cla.GetInt64FromPath(idParam)
if err != nil { if err != nil {
cla.SendInternalServerError(err) cla.SendInternalServerError(err)

View File

@ -15,6 +15,7 @@ import (
"github.com/goharbor/harbor/src/core/label" "github.com/goharbor/harbor/src/core/label"
"github.com/goharbor/harbor/src/chartserver" "github.com/goharbor/harbor/src/chartserver"
"github.com/goharbor/harbor/src/common/rbac"
hlog "github.com/goharbor/harbor/src/common/utils/log" hlog "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
) )
@ -84,10 +85,35 @@ func (cra *ChartRepositoryAPI) Prepare() {
cra.labelManager = &label.BaseManager{} cra.labelManager = &label.BaseManager{}
} }
func (cra *ChartRepositoryAPI) requireAccess(action rbac.Action, subresource ...rbac.Resource) bool {
if len(subresource) == 0 {
subresource = append(subresource, rbac.ResourceHelmChart)
}
resource := rbac.NewProjectNamespace(cra.namespace).Resource(subresource...)
if !cra.SecurityCtx.Can(action, resource) {
if !cra.SecurityCtx.IsAuthenticated() {
cra.SendUnAuthorizedError(errors.New("Unauthorized"))
} else {
cra.HandleForbidden(cra.SecurityCtx.GetUsername())
}
return false
}
return true
}
// GetHealthStatus handles GET /api/chartrepo/health // GetHealthStatus handles GET /api/chartrepo/health
func (cra *ChartRepositoryAPI) GetHealthStatus() { func (cra *ChartRepositoryAPI) GetHealthStatus() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelSystem) { if !cra.SecurityCtx.IsAuthenticated() {
cra.SendUnAuthorizedError(errors.New("Unauthorized"))
return
}
if !cra.SecurityCtx.IsSysAdmin() {
cra.HandleForbidden(cra.SecurityCtx.GetUsername())
return return
} }
@ -98,7 +124,7 @@ func (cra *ChartRepositoryAPI) GetHealthStatus() {
// GetIndexByRepo handles GET /:repo/index.yaml // GetIndexByRepo handles GET /:repo/index.yaml
func (cra *ChartRepositoryAPI) GetIndexByRepo() { func (cra *ChartRepositoryAPI) GetIndexByRepo() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelRead) { if !cra.requireAccess(rbac.ActionRead) {
return return
} }
@ -109,7 +135,13 @@ func (cra *ChartRepositoryAPI) GetIndexByRepo() {
// GetIndex handles GET /index.yaml // GetIndex handles GET /index.yaml
func (cra *ChartRepositoryAPI) GetIndex() { func (cra *ChartRepositoryAPI) GetIndex() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelSystem) { if !cra.SecurityCtx.IsAuthenticated() {
cra.SendUnAuthorizedError(errors.New("Unauthorized"))
return
}
if !cra.SecurityCtx.IsSysAdmin() {
cra.HandleForbidden(cra.SecurityCtx.GetUsername())
return return
} }
@ -136,7 +168,7 @@ func (cra *ChartRepositoryAPI) GetIndex() {
// DownloadChart handles GET /:repo/charts/:filename // DownloadChart handles GET /:repo/charts/:filename
func (cra *ChartRepositoryAPI) DownloadChart() { func (cra *ChartRepositoryAPI) DownloadChart() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelRead) { if !cra.requireAccess(rbac.ActionRead) {
return return
} }
@ -147,7 +179,7 @@ func (cra *ChartRepositoryAPI) DownloadChart() {
// ListCharts handles GET /api/:repo/charts // ListCharts handles GET /api/:repo/charts
func (cra *ChartRepositoryAPI) ListCharts() { func (cra *ChartRepositoryAPI) ListCharts() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelRead) { if !cra.requireAccess(rbac.ActionList) {
return return
} }
@ -163,7 +195,7 @@ func (cra *ChartRepositoryAPI) ListCharts() {
// ListChartVersions GET /api/:repo/charts/:name // ListChartVersions GET /api/:repo/charts/:name
func (cra *ChartRepositoryAPI) ListChartVersions() { func (cra *ChartRepositoryAPI) ListChartVersions() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelRead) { if !cra.requireAccess(rbac.ActionList, rbac.ResourceHelmChartVersion) {
return return
} }
@ -191,7 +223,7 @@ func (cra *ChartRepositoryAPI) ListChartVersions() {
// GetChartVersion handles GET /api/:repo/charts/:name/:version // GetChartVersion handles GET /api/:repo/charts/:name/:version
func (cra *ChartRepositoryAPI) GetChartVersion() { func (cra *ChartRepositoryAPI) GetChartVersion() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelRead) { if !cra.requireAccess(rbac.ActionRead, rbac.ResourceHelmChartVersion) {
return return
} }
@ -219,7 +251,7 @@ func (cra *ChartRepositoryAPI) GetChartVersion() {
// DeleteChartVersion handles DELETE /api/:repo/charts/:name/:version // DeleteChartVersion handles DELETE /api/:repo/charts/:name/:version
func (cra *ChartRepositoryAPI) DeleteChartVersion() { func (cra *ChartRepositoryAPI) DeleteChartVersion() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelAll) { if !cra.requireAccess(rbac.ActionDelete, rbac.ResourceHelmChartVersion) {
return return
} }
@ -244,7 +276,7 @@ func (cra *ChartRepositoryAPI) UploadChartVersion() {
hlog.Debugf("Header of request of uploading chart: %#v, content-len=%d", cra.Ctx.Request.Header, cra.Ctx.Request.ContentLength) hlog.Debugf("Header of request of uploading chart: %#v, content-len=%d", cra.Ctx.Request.Header, cra.Ctx.Request.ContentLength)
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelWrite) { if !cra.requireAccess(rbac.ActionCreate, rbac.ResourceHelmChartVersion) {
return return
} }
@ -272,7 +304,7 @@ func (cra *ChartRepositoryAPI) UploadChartVersion() {
// UploadChartProvFile handles POST /api/:repo/prov // UploadChartProvFile handles POST /api/:repo/prov
func (cra *ChartRepositoryAPI) UploadChartProvFile() { func (cra *ChartRepositoryAPI) UploadChartProvFile() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelWrite) { if !cra.requireAccess(rbac.ActionCreate) {
return return
} }
@ -297,7 +329,7 @@ func (cra *ChartRepositoryAPI) UploadChartProvFile() {
// DeleteChart deletes all the chart versions of the specified chart. // DeleteChart deletes all the chart versions of the specified chart.
func (cra *ChartRepositoryAPI) DeleteChart() { func (cra *ChartRepositoryAPI) DeleteChart() {
// Check access // Check access
if !cra.requireAccess(cra.namespace, accessLevelWrite) { if !cra.requireAccess(rbac.ActionDelete) {
return return
} }
@ -365,62 +397,6 @@ func (cra *ChartRepositoryAPI) requireNamespace(namespace string) bool {
return true return true
} }
// Check if the related access match the expected requirement
// If with right access, return true
// If without right access, return false
func (cra *ChartRepositoryAPI) requireAccess(namespace string, accessLevel uint) bool {
if accessLevel == accessLevelPublic {
return true // do nothing
}
theLevel := accessLevel
// If repo is empty, system admin role must be required
if len(namespace) == 0 {
theLevel = accessLevelSystem
}
var err error
switch theLevel {
// Should be system admin role
case accessLevelSystem:
if !cra.SecurityCtx.IsSysAdmin() {
err = errors.New("permission denied: system admin role is required")
}
case accessLevelAll:
if !cra.SecurityCtx.HasAllPerm(namespace) {
err = errors.New("permission denied: project admin or higher role is required")
}
case accessLevelWrite:
if !cra.SecurityCtx.HasWritePerm(namespace) {
err = errors.New("permission denied: developer or higher role is required")
}
case accessLevelRead:
if !cra.SecurityCtx.HasReadPerm(namespace) {
err = errors.New("permission denied: guest or higher role is required")
}
default:
// access rejected for invalid scope
cra.SendForbiddenError(errors.New("unrecognized access scope"))
return false
}
// Access is not granted, check if user has authenticated
if err != nil {
// Unauthenticated, return 401
if !cra.SecurityCtx.IsAuthenticated() {
cra.SendUnAuthorizedError(errors.New("Unauthorized"))
return false
}
// Authenticated, return 403
cra.SendForbiddenError(err)
return false
}
return true
}
// formFile is used to represent the uploaded files in the form // formFile is used to represent the uploaded files in the form
type formFile struct { type formFile struct {
// form field key contains the form file // form field key contains the form file

View File

@ -17,29 +17,6 @@ var (
crMockServer *httptest.Server crMockServer *httptest.Server
) )
// Test access checking
func TestRequireAccess(t *testing.T) {
chartAPI := &ChartRepositoryAPI{}
chartAPI.SecurityCtx = &mockSecurityContext{}
ns := "library"
if !chartAPI.requireAccess(ns, accessLevelPublic) {
t.Fatal("expect true result (public access level is granted) but got false")
}
if !chartAPI.requireAccess(ns, accessLevelAll) {
t.Fatal("expect true result (admin has all perm) but got false")
}
if !chartAPI.requireAccess(ns, accessLevelRead) {
t.Fatal("expect true result (admin has read perm) but got false")
}
if !chartAPI.requireAccess(ns, accessLevelWrite) {
t.Fatal("expect true result (admin has write perm) but got false")
}
if !chartAPI.requireAccess(ns, accessLevelSystem) {
t.Fatal("expect true result (admin has system perm) but got false")
}
}
func TestIsMultipartFormData(t *testing.T) { func TestIsMultipartFormData(t *testing.T) {
req, err := createRequest(http.MethodPost, "/api/chartrepo/charts") req, err := createRequest(http.MethodPost, "/api/chartrepo/charts")
if err != nil { if err != nil {
@ -205,7 +182,7 @@ func TestDeleteChart(t *testing.T) {
request: &testingRequest{ request: &testingRequest{
url: "/api/chartrepo/library/charts/harbor", url: "/api/chartrepo/library/charts/harbor",
method: http.MethodDelete, method: http.MethodDelete,
credential: projDeveloper, credential: projAdmin,
}, },
code: http.StatusOK, code: http.StatusOK,
}) })
@ -310,21 +287,6 @@ func (msc *mockSecurityContext) IsSolutionUser() bool {
return false return false
} }
// HasReadPerm returns whether the user has read permission to the project
func (msc *mockSecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
return msc.Can(rbac.ActionPull, rbac.NewProjectNamespace(projectIDOrName, false).Resource(rbac.ResourceRepository))
}
// HasWritePerm returns whether the user has write permission to the project
func (msc *mockSecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
return msc.Can(rbac.ActionPush, rbac.NewProjectNamespace(projectIDOrName, false).Resource(rbac.ResourceRepository))
}
// HasAllPerm returns whether the user has all permissions to the project
func (msc *mockSecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return msc.HasReadPerm(projectIDOrName) && msc.HasWritePerm(projectIDOrName)
}
// Can returns whether the user can do action on resource // Can returns whether the user can do action on resource
func (msc *mockSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool { func (msc *mockSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
namespace, err := resource.GetNamespace() namespace, err := resource.GetNamespace()

View File

@ -22,6 +22,7 @@ import (
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"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/replication" "github.com/goharbor/harbor/src/replication"
"github.com/goharbor/harbor/src/replication/core" "github.com/goharbor/harbor/src/replication/core"
rep_models "github.com/goharbor/harbor/src/replication/models" rep_models "github.com/goharbor/harbor/src/replication/models"
@ -65,15 +66,36 @@ func (l *LabelAPI) Prepare() {
return return
} }
if label.Scope == common.LabelScopeGlobal && !l.SecurityCtx.IsSysAdmin() ||
label.Scope == common.LabelScopeProject && !l.SecurityCtx.HasAllPerm(label.ProjectID) {
l.HandleForbidden(l.SecurityCtx.GetUsername())
return
}
l.label = label l.label = label
} }
} }
func (l *LabelAPI) requireAccess(label *models.Label, action rbac.Action, subresources ...rbac.Resource) bool {
var hasPermission bool
switch label.Scope {
case common.LabelScopeGlobal:
hasPermission = l.SecurityCtx.IsSysAdmin()
case common.LabelScopeProject:
if len(subresources) == 0 {
subresources = append(subresources, rbac.ResourceLabel)
}
resource := rbac.NewProjectNamespace(label.ProjectID).Resource(subresources...)
hasPermission = l.SecurityCtx.Can(action, resource)
}
if !hasPermission {
if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized()
} else {
l.HandleForbidden(l.SecurityCtx.GetUsername())
}
return false
}
return true
}
// Post creates a label // Post creates a label
func (l *LabelAPI) Post() { func (l *LabelAPI) Post() {
label := &models.Label{} label := &models.Label{}
@ -82,10 +104,6 @@ func (l *LabelAPI) Post() {
switch label.Scope { switch label.Scope {
case common.LabelScopeGlobal: case common.LabelScopeGlobal:
if !l.SecurityCtx.IsSysAdmin() {
l.HandleForbidden(l.SecurityCtx.GetUsername())
return
}
label.ProjectID = 0 label.ProjectID = 0
case common.LabelScopeProject: case common.LabelScopeProject:
exist, err := l.ProjectMgr.Exists(label.ProjectID) exist, err := l.ProjectMgr.Exists(label.ProjectID)
@ -98,10 +116,10 @@ func (l *LabelAPI) Post() {
l.HandleNotFound(fmt.Sprintf("project %d not found", label.ProjectID)) l.HandleNotFound(fmt.Sprintf("project %d not found", label.ProjectID))
return return
} }
if !l.SecurityCtx.HasAllPerm(label.ProjectID) { }
l.HandleForbidden(l.SecurityCtx.GetUsername())
return if !l.requireAccess(label, rbac.ActionCreate) {
} return
} }
labels, err := dao.ListLabels(&models.LabelQuery{ labels, err := dao.ListLabels(&models.LabelQuery{
@ -147,15 +165,8 @@ func (l *LabelAPI) Get() {
return return
} }
if label.Scope == common.LabelScopeProject { if !l.requireAccess(label, rbac.ActionRead) {
if !l.SecurityCtx.HasReadPerm(label.ProjectID) { return
if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized()
return
}
l.HandleForbidden(l.SecurityCtx.GetUsername())
return
}
} }
l.Data["json"] = label l.Data["json"] = label
@ -189,7 +200,8 @@ func (l *LabelAPI) List() {
return return
} }
if !l.SecurityCtx.HasReadPerm(projectID) { resource := rbac.NewProjectNamespace(projectID).Resource(rbac.ResourceLabel)
if !l.SecurityCtx.Can(rbac.ActionList, resource) {
if !l.SecurityCtx.IsAuthenticated() { if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized() l.HandleUnauthorized()
return return
@ -221,6 +233,10 @@ func (l *LabelAPI) List() {
// Put updates the label // Put updates the label
func (l *LabelAPI) Put() { func (l *LabelAPI) Put() {
if !l.requireAccess(l.label, rbac.ActionUpdate) {
return
}
label := &models.Label{} label := &models.Label{}
l.DecodeJSONReq(label) l.DecodeJSONReq(label)
@ -259,6 +275,10 @@ func (l *LabelAPI) Put() {
// Delete the label // Delete the label
func (l *LabelAPI) Delete() { func (l *LabelAPI) Delete() {
if !l.requireAccess(l.label, rbac.ActionDelete) {
return
}
id := l.label.ID id := l.label.ID
if err := dao.DeleteResourceLabelByLabel(id); err != nil { if err := dao.DeleteResourceLabelByLabel(id); err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to delete resource label mappings of label %d: %v", id, err)) l.HandleInternalServerError(fmt.Sprintf("failed to delete resource label mappings of label %d: %v", id, err))
@ -272,11 +292,6 @@ func (l *LabelAPI) Delete() {
// ListResources lists the resources that the label is referenced by // ListResources lists the resources that the label is referenced by
func (l *LabelAPI) ListResources() { func (l *LabelAPI) ListResources() {
if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized()
return
}
id, err := l.GetInt64FromPath(":id") id, err := l.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
l.HandleBadRequest("invalid label ID") l.HandleBadRequest("invalid label ID")
@ -294,9 +309,7 @@ func (l *LabelAPI) ListResources() {
return return
} }
if label.Scope == common.LabelScopeGlobal && !l.SecurityCtx.IsSysAdmin() || if !l.requireAccess(label, rbac.ActionList, rbac.ResourceLabelResource) {
label.Scope == common.LabelScopeProject && !l.SecurityCtx.HasAllPerm(label.ProjectID) {
l.HandleForbidden(l.SecurityCtx.GetUsername())
return return
} }

View File

@ -22,23 +22,6 @@ func (lra *LabelResourceAPI) Prepare() {
lra.labelManager = &label.BaseManager{} lra.labelManager = &label.BaseManager{}
} }
func (lra *LabelResourceAPI) checkPermissions(project string) bool {
if lra.Ctx.Request.Method == http.MethodPost ||
lra.Ctx.Request.Method == http.MethodDelete {
if lra.SecurityCtx.HasWritePerm(project) {
return true
}
}
if lra.Ctx.Request.Method == http.MethodGet {
if lra.SecurityCtx.HasReadPerm(project) {
return true
}
}
return false
}
func (lra *LabelResourceAPI) getLabelsOfResource(rType string, rIDOrName interface{}) { func (lra *LabelResourceAPI) getLabelsOfResource(rType string, rIDOrName interface{}) {
labels, err := lra.labelManager.GetLabelsOfResource(rType, rIDOrName) labels, err := lra.labelManager.GetLabelsOfResource(rType, rIDOrName)
if err != nil { if err != nil {

View File

@ -22,6 +22,7 @@ import (
"strings" "strings"
"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/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/promgr/metamgr" "github.com/goharbor/harbor/src/core/promgr/metamgr"
) )
@ -72,24 +73,6 @@ func (m *MetadataAPI) Prepare() {
m.project = project m.project = project
switch m.Ctx.Request.Method {
case http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete:
if !(m.Ctx.Request.Method == http.MethodGet && project.IsPublic()) {
if !m.SecurityCtx.IsAuthenticated() {
m.HandleUnauthorized()
return
}
if !m.SecurityCtx.HasReadPerm(project.ProjectID) {
m.HandleForbidden(m.SecurityCtx.GetUsername())
return
}
}
default:
log.Debugf("%s method not allowed", m.Ctx.Request.Method)
m.RenderError(http.StatusMethodNotAllowed, "")
return
}
name := m.GetStringFromPath(":name") name := m.GetStringFromPath(":name")
if len(name) > 0 { if len(name) > 0 {
m.name = name m.name = name
@ -105,8 +88,27 @@ func (m *MetadataAPI) Prepare() {
} }
} }
func (m *MetadataAPI) requireAccess(action rbac.Action) bool {
resource := rbac.NewProjectNamespace(m.project.ProjectID).Resource(rbac.ResourceMetadata)
if !m.SecurityCtx.Can(action, resource) {
if !m.SecurityCtx.IsAuthenticated() {
m.HandleUnauthorized()
} else {
m.HandleForbidden(m.SecurityCtx.GetUsername())
}
return false
}
return true
}
// Get ... // Get ...
func (m *MetadataAPI) Get() { func (m *MetadataAPI) Get() {
if !m.requireAccess(rbac.ActionRead) {
return
}
var metas map[string]string var metas map[string]string
var err error var err error
if len(m.name) > 0 { if len(m.name) > 0 {
@ -125,6 +127,10 @@ func (m *MetadataAPI) Get() {
// Post ... // Post ...
func (m *MetadataAPI) Post() { func (m *MetadataAPI) Post() {
if !m.requireAccess(rbac.ActionCreate) {
return
}
var metas map[string]string var metas map[string]string
m.DecodeJSONReq(&metas) m.DecodeJSONReq(&metas)
@ -161,6 +167,10 @@ func (m *MetadataAPI) Post() {
// Put ... // Put ...
func (m *MetadataAPI) Put() { func (m *MetadataAPI) Put() {
if !m.requireAccess(rbac.ActionUpdate) {
return
}
var metas map[string]string var metas map[string]string
m.DecodeJSONReq(&metas) m.DecodeJSONReq(&metas)
@ -188,6 +198,10 @@ func (m *MetadataAPI) Put() {
// Delete ... // Delete ...
func (m *MetadataAPI) Delete() { func (m *MetadataAPI) Delete() {
if !m.requireAccess(rbac.ActionDelete) {
return
}
if err := m.metaMgr.Delete(m.project.ProjectID, m.name); err != nil { if err := m.metaMgr.Delete(m.project.ProjectID, m.name); err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to delete metadata %s of project %d: %v", m.name, m.project.ProjectID, err)) m.HandleInternalServerError(fmt.Sprintf("failed to delete metadata %s of project %d: %v", m.name, m.project.ProjectID, err))
return return

View File

@ -22,6 +22,7 @@ import (
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"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/utils" "github.com/goharbor/harbor/src/common/utils"
errutil "github.com/goharbor/harbor/src/common/utils/error" errutil "github.com/goharbor/harbor/src/common/utils/error"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
@ -77,6 +78,25 @@ func (p *ProjectAPI) Prepare() {
} }
} }
func (p *ProjectAPI) requireAccess(action rbac.Action, subresource ...rbac.Resource) bool {
if len(subresource) == 0 {
subresource = append(subresource, rbac.ResourceSelf)
}
resource := rbac.NewProjectNamespace(p.project.ProjectID).Resource(subresource...)
if !p.SecurityCtx.Can(action, resource) {
if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized()
} else {
p.HandleForbidden(p.SecurityCtx.GetUsername())
}
return false
}
return true
}
// Post ... // Post ...
func (p *ProjectAPI) Post() { func (p *ProjectAPI) Post() {
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
@ -187,16 +207,8 @@ func (p *ProjectAPI) Head() {
// Get ... // Get ...
func (p *ProjectAPI) Get() { func (p *ProjectAPI) Get() {
if !p.project.IsPublic() { if !p.requireAccess(rbac.ActionRead) {
if !p.SecurityCtx.IsAuthenticated() { return
p.HandleUnauthorized()
return
}
if !p.SecurityCtx.HasReadPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername())
return
}
} }
p.populateProperties(p.project) p.populateProperties(p.project)
@ -207,13 +219,7 @@ func (p *ProjectAPI) Get() {
// Delete ... // Delete ...
func (p *ProjectAPI) Delete() { func (p *ProjectAPI) Delete() {
if !p.SecurityCtx.IsAuthenticated() { if !p.requireAccess(rbac.ActionDelete) {
p.HandleUnauthorized()
return
}
if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
@ -248,13 +254,7 @@ func (p *ProjectAPI) Delete() {
// Deletable ... // Deletable ...
func (p *ProjectAPI) Deletable() { func (p *ProjectAPI) Deletable() {
if !p.SecurityCtx.IsAuthenticated() { if !p.requireAccess(rbac.ActionDelete) {
p.HandleUnauthorized()
return
}
if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
@ -433,13 +433,7 @@ func (p *ProjectAPI) populateProperties(project *models.Project) {
// Put ... // Put ...
func (p *ProjectAPI) Put() { func (p *ProjectAPI) Put() {
if !p.SecurityCtx.IsAuthenticated() { if !p.requireAccess(rbac.ActionUpdate) {
p.HandleUnauthorized()
return
}
if !p.SecurityCtx.HasAllPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }
@ -458,13 +452,7 @@ func (p *ProjectAPI) Put() {
// Logs ... // Logs ...
func (p *ProjectAPI) Logs() { func (p *ProjectAPI) Logs() {
if !p.SecurityCtx.IsAuthenticated() { if !p.requireAccess(rbac.ActionList, rbac.ResourceLog) {
p.HandleUnauthorized()
return
}
if !p.SecurityCtx.HasReadPerm(p.project.ProjectID) {
p.HandleForbidden(p.SecurityCtx.GetUsername())
return return
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/project" "github.com/goharbor/harbor/src/common/dao/project"
"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/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/auth" "github.com/goharbor/harbor/src/core/auth"
) )
@ -73,12 +74,6 @@ func (pma *ProjectMemberAPI) Prepare() {
} }
pma.project = project pma.project = project
if !(pma.Ctx.Input.IsGet() && pma.SecurityCtx.HasReadPerm(pid) ||
pma.SecurityCtx.HasAllPerm(pid)) {
pma.HandleForbidden(pma.SecurityCtx.GetUsername())
return
}
pmid, err := pma.GetInt64FromPath(":pmid") pmid, err := pma.GetInt64FromPath(":pmid")
if err != nil { if err != nil {
log.Warningf("Failed to get pmid from path, error %v", err) log.Warningf("Failed to get pmid from path, error %v", err)
@ -90,6 +85,22 @@ func (pma *ProjectMemberAPI) Prepare() {
pma.id = int(pmid) pma.id = int(pmid)
} }
func (pma *ProjectMemberAPI) requireAccess(action rbac.Action) bool {
resource := rbac.NewProjectNamespace(pma.project.ProjectID).Resource(rbac.ResourceMember)
if !pma.SecurityCtx.Can(action, resource) {
if !pma.SecurityCtx.IsAuthenticated() {
pma.HandleUnauthorized()
} else {
pma.HandleForbidden(pma.SecurityCtx.GetUsername())
}
return false
}
return true
}
// Get ... // Get ...
func (pma *ProjectMemberAPI) Get() { func (pma *ProjectMemberAPI) Get() {
projectID := pma.project.ProjectID projectID := pma.project.ProjectID
@ -97,6 +108,9 @@ func (pma *ProjectMemberAPI) Get() {
queryMember.ProjectID = projectID queryMember.ProjectID = projectID
pma.Data["json"] = make([]models.Member, 0) pma.Data["json"] = make([]models.Member, 0)
if pma.id == 0 { if pma.id == 0 {
if !pma.requireAccess(rbac.ActionList) {
return
}
entityname := pma.GetString("entityname") entityname := pma.GetString("entityname")
memberList, err := project.SearchMemberByName(projectID, entityname) memberList, err := project.SearchMemberByName(projectID, entityname)
if err != nil { if err != nil {
@ -119,6 +133,10 @@ func (pma *ProjectMemberAPI) Get() {
pma.HandleNotFound(fmt.Sprintf("The project member does not exit, pmid:%v", pma.id)) pma.HandleNotFound(fmt.Sprintf("The project member does not exit, pmid:%v", pma.id))
return return
} }
if !pma.requireAccess(rbac.ActionRead) {
return
}
pma.Data["json"] = memberList[0] pma.Data["json"] = memberList[0]
} }
pma.ServeJSON() pma.ServeJSON()
@ -126,6 +144,9 @@ func (pma *ProjectMemberAPI) Get() {
// Post ... Add a project member // Post ... Add a project member
func (pma *ProjectMemberAPI) Post() { func (pma *ProjectMemberAPI) Post() {
if !pma.requireAccess(rbac.ActionCreate) {
return
}
projectID := pma.project.ProjectID projectID := pma.project.ProjectID
var request models.MemberReq var request models.MemberReq
pma.DecodeJSONReq(&request) pma.DecodeJSONReq(&request)
@ -156,6 +177,9 @@ func (pma *ProjectMemberAPI) Post() {
// Put ... Update an exist project member // Put ... Update an exist project member
func (pma *ProjectMemberAPI) Put() { func (pma *ProjectMemberAPI) Put() {
if !pma.requireAccess(rbac.ActionUpdate) {
return
}
pid := pma.project.ProjectID pid := pma.project.ProjectID
pmID := pma.id pmID := pma.id
var req models.Member var req models.Member
@ -173,6 +197,9 @@ func (pma *ProjectMemberAPI) Put() {
// Delete ... // Delete ...
func (pma *ProjectMemberAPI) Delete() { func (pma *ProjectMemberAPI) Delete() {
if !pma.requireAccess(rbac.ActionDelete) {
return
}
pmid := pma.id pmid := pma.id
err := project.DeleteProjectMemberByID(pmid) err := project.DeleteProjectMemberByID(pmid)
if err != nil { if err != nil {

View File

@ -24,6 +24,7 @@ import (
common_http "github.com/goharbor/harbor/src/common/http" common_http "github.com/goharbor/harbor/src/common/http"
common_job "github.com/goharbor/harbor/src/common/job" common_job "github.com/goharbor/harbor/src/common/job"
"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/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
api_models "github.com/goharbor/harbor/src/core/api/models" api_models "github.com/goharbor/harbor/src/core/api/models"
"github.com/goharbor/harbor/src/core/utils" "github.com/goharbor/harbor/src/core/utils"
@ -80,7 +81,8 @@ func (ra *RepJobAPI) List() {
return return
} }
if !ra.SecurityCtx.HasAllPerm(policy.ProjectIDs[0]) { resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplicationJob)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return return
} }
@ -190,7 +192,8 @@ func (ra *RepJobAPI) GetLog() {
return return
} }
if !ra.SecurityCtx.HasAllPerm(policy.ProjectIDs[0]) { resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplicationJob)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return return
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"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/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
api_models "github.com/goharbor/harbor/src/core/api/models" api_models "github.com/goharbor/harbor/src/core/api/models"
"github.com/goharbor/harbor/src/core/promgr" "github.com/goharbor/harbor/src/core/promgr"
@ -63,7 +64,8 @@ func (pa *RepPolicyAPI) Get() {
return return
} }
if !pa.SecurityCtx.HasAllPerm(policy.ProjectIDs[0]) { resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplication)
if !pa.SecurityCtx.Can(rbac.ActionRead, resource) {
pa.HandleForbidden(pa.SecurityCtx.GetUsername()) pa.HandleForbidden(pa.SecurityCtx.GetUsername())
return return
} }
@ -105,7 +107,8 @@ func (pa *RepPolicyAPI) List() {
if result != nil { if result != nil {
total = result.Total total = result.Total
for _, policy := range result.Policies { for _, policy := range result.Policies {
if !pa.SecurityCtx.HasAllPerm(policy.ProjectIDs[0]) { resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplication)
if !pa.SecurityCtx.Can(rbac.ActionRead, resource) {
continue continue
} }
ply, err := convertFromRepPolicy(pa.ProjectMgr, *policy) ply, err := convertFromRepPolicy(pa.ProjectMgr, *policy)

View File

@ -30,6 +30,7 @@ import (
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
commonhttp "github.com/goharbor/harbor/src/common/http" commonhttp "github.com/goharbor/harbor/src/common/http"
"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/utils" "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/clair" "github.com/goharbor/harbor/src/common/utils/clair"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
@ -131,7 +132,8 @@ func (ra *RepositoryAPI) Get() {
return return
} }
if !ra.SecurityCtx.HasReadPerm(projectID) { resource := rbac.NewProjectNamespace(projectID).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return
@ -247,7 +249,8 @@ func (ra *RepositoryAPI) Delete() {
return return
} }
if !ra.SecurityCtx.HasAllPerm(projectName) { resource := rbac.NewProjectNamespace(project.ProjectID).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionDelete, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return return
} }
@ -393,7 +396,8 @@ func (ra *RepositoryAPI) GetTag() {
return return
} }
project, _ := utils.ParseRepository(repository) project, _ := utils.ParseRepository(repository)
if !ra.SecurityCtx.HasReadPerm(project) { resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepositoryTag)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return
@ -488,14 +492,16 @@ func (ra *RepositoryAPI) Retag() {
} }
// Check whether user has read permission to source project // Check whether user has read permission to source project
if !ra.SecurityCtx.HasReadPerm(srcImage.Project) { srcResource := rbac.NewProjectNamespace(srcImage.Project).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionPull, srcResource) {
log.Errorf("user has no read permission to project '%s'", srcImage.Project) log.Errorf("user has no read permission to project '%s'", srcImage.Project)
ra.HandleForbidden(fmt.Sprintf("%s has no read permission to project %s", ra.SecurityCtx.GetUsername(), srcImage.Project)) ra.HandleForbidden(fmt.Sprintf("%s has no read permission to project %s", ra.SecurityCtx.GetUsername(), srcImage.Project))
return return
} }
// Check whether user has write permission to target project // Check whether user has write permission to target project
if !ra.SecurityCtx.HasWritePerm(project) { destResource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionPush, destResource) {
log.Errorf("user has no write permission to project '%s'", project) log.Errorf("user has no write permission to project '%s'", project)
ra.HandleForbidden(fmt.Sprintf("%s has no write permission to project %s", ra.SecurityCtx.GetUsername(), project)) ra.HandleForbidden(fmt.Sprintf("%s has no write permission to project %s", ra.SecurityCtx.GetUsername(), project))
return return
@ -533,7 +539,8 @@ func (ra *RepositoryAPI) GetTags() {
return return
} }
if !ra.SecurityCtx.HasReadPerm(projectName) { resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTag)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return
@ -741,7 +748,8 @@ func (ra *RepositoryAPI) GetManifests() {
return return
} }
if !ra.SecurityCtx.HasReadPerm(projectName) { resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagManifest)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return
@ -872,7 +880,8 @@ func (ra *RepositoryAPI) Put() {
} }
project, _ := utils.ParseRepository(name) project, _ := utils.ParseRepository(name)
if !ra.SecurityCtx.HasWritePerm(project) { resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionUpdate, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return return
} }
@ -906,7 +915,8 @@ func (ra *RepositoryAPI) GetSignatures() {
return return
} }
if !ra.SecurityCtx.HasReadPerm(projectName) { resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return
@ -949,7 +959,9 @@ func (ra *RepositoryAPI) ScanImage() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return
} }
if !ra.SecurityCtx.HasAllPerm(projectName) {
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagScanJob)
if !ra.SecurityCtx.Can(rbac.ActionCreate, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return return
} }
@ -980,7 +992,9 @@ func (ra *RepositoryAPI) VulnerabilityDetails() {
return return
} }
project, _ := utils.ParseRepository(repository) project, _ := utils.ParseRepository(repository)
if !ra.SecurityCtx.HasReadPerm(project) {
resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepositoryTagVulnerability)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.HandleUnauthorized()
return return

View File

@ -22,7 +22,7 @@ import (
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/rbac"
coreutils "github.com/goharbor/harbor/src/core/utils" coreutils "github.com/goharbor/harbor/src/core/utils"
) )
@ -45,12 +45,6 @@ func (r *RepositoryLabelAPI) Prepare() {
} }
repository := r.GetString(":splat") repository := r.GetString(":splat")
project, _ := utils.ParseRepository(repository)
if !r.checkPermissions(project) {
r.SendForbiddenError(errors.New(r.SecurityCtx.GetUsername()))
return
}
repo, err := dao.GetRepositoryByName(repository) repo, err := dao.GetRepositoryByName(repository)
if err != nil { if err != nil {
r.SendInternalServerError(fmt.Errorf("failed to get repository %s: %v", repository, err)) r.SendInternalServerError(fmt.Errorf("failed to get repository %s: %v", repository, err))
@ -77,25 +71,6 @@ func (r *RepositoryLabelAPI) Prepare() {
r.tag = tag r.tag = tag
} }
if r.Ctx.Request.Method == http.MethodPost {
p, err := r.ProjectMgr.Get(project)
if err != nil {
r.SendInternalServerError(err)
return
}
l := &models.Label{}
r.DecodeJSONReq(l)
label, ok := r.validate(l.ID, p.ProjectID)
if !ok {
return
}
r.label = label
return
}
if r.Ctx.Request.Method == http.MethodDelete { if r.Ctx.Request.Method == http.MethodDelete {
labelID, err := r.GetInt64FromPath(":id") labelID, err := r.GetInt64FromPath(":id")
if err != nil { if err != nil {
@ -112,13 +87,59 @@ func (r *RepositoryLabelAPI) Prepare() {
} }
} }
func (r *RepositoryLabelAPI) requireAccess(action rbac.Action, subresource ...rbac.Resource) bool {
if len(subresource) == 0 {
subresource = append(subresource, rbac.ResourceRepositoryLabel)
}
resource := rbac.NewProjectNamespace(r.repository.ProjectID).Resource(rbac.ResourceRepositoryLabel)
if !r.SecurityCtx.Can(action, resource) {
if !r.SecurityCtx.IsAuthenticated() {
r.SendUnAuthorizedError(errors.New("UnAuthorized"))
} else {
r.SendForbiddenError(errors.New(r.SecurityCtx.GetUsername()))
}
return false
}
return true
}
func (r *RepositoryLabelAPI) isValidLabelReq() bool {
p, err := r.ProjectMgr.Get(r.repository.ProjectID)
if err != nil {
r.SendInternalServerError(err)
return false
}
l := &models.Label{}
r.DecodeJSONReq(l)
label, ok := r.validate(l.ID, p.ProjectID)
if !ok {
return false
}
r.label = label
return true
}
// GetOfImage returns labels of an image // GetOfImage returns labels of an image
func (r *RepositoryLabelAPI) GetOfImage() { func (r *RepositoryLabelAPI) GetOfImage() {
if !r.requireAccess(rbac.ActionList, rbac.ResourceRepositoryTagLabel) {
return
}
r.getLabelsOfResource(common.ResourceTypeImage, fmt.Sprintf("%s:%s", r.repository.Name, r.tag)) r.getLabelsOfResource(common.ResourceTypeImage, fmt.Sprintf("%s:%s", r.repository.Name, r.tag))
} }
// AddToImage adds the label to an image // AddToImage adds the label to an image
func (r *RepositoryLabelAPI) AddToImage() { func (r *RepositoryLabelAPI) AddToImage() {
if !r.requireAccess(rbac.ActionCreate, rbac.ResourceRepositoryTagLabel) || !r.isValidLabelReq() {
return
}
rl := &models.ResourceLabel{ rl := &models.ResourceLabel{
LabelID: r.label.ID, LabelID: r.label.ID,
ResourceType: common.ResourceTypeImage, ResourceType: common.ResourceTypeImage,
@ -129,17 +150,29 @@ func (r *RepositoryLabelAPI) AddToImage() {
// RemoveFromImage removes the label from an image // RemoveFromImage removes the label from an image
func (r *RepositoryLabelAPI) RemoveFromImage() { func (r *RepositoryLabelAPI) RemoveFromImage() {
if !r.requireAccess(rbac.ActionDelete, rbac.ResourceRepositoryTagLabel) {
return
}
r.removeLabelFromResource(common.ResourceTypeImage, r.removeLabelFromResource(common.ResourceTypeImage,
fmt.Sprintf("%s:%s", r.repository.Name, r.tag), r.label.ID) fmt.Sprintf("%s:%s", r.repository.Name, r.tag), r.label.ID)
} }
// GetOfRepository returns labels of a repository // GetOfRepository returns labels of a repository
func (r *RepositoryLabelAPI) GetOfRepository() { func (r *RepositoryLabelAPI) GetOfRepository() {
if !r.requireAccess(rbac.ActionList) {
return
}
r.getLabelsOfResource(common.ResourceTypeRepository, r.repository.RepositoryID) r.getLabelsOfResource(common.ResourceTypeRepository, r.repository.RepositoryID)
} }
// AddToRepository adds the label to a repository // AddToRepository adds the label to a repository
func (r *RepositoryLabelAPI) AddToRepository() { func (r *RepositoryLabelAPI) AddToRepository() {
if !r.requireAccess(rbac.ActionCreate) || !r.isValidLabelReq() {
return
}
rl := &models.ResourceLabel{ rl := &models.ResourceLabel{
LabelID: r.label.ID, LabelID: r.label.ID,
ResourceType: common.ResourceTypeRepository, ResourceType: common.ResourceTypeRepository,
@ -150,6 +183,10 @@ func (r *RepositoryLabelAPI) AddToRepository() {
// RemoveFromRepository removes the label from a repository // RemoveFromRepository removes the label from a repository
func (r *RepositoryLabelAPI) RemoveFromRepository() { func (r *RepositoryLabelAPI) RemoveFromRepository() {
if !r.requireAccess(rbac.ActionDelete) {
return
}
r.removeLabelFromResource(common.ResourceTypeRepository, r.repository.RepositoryID, r.label.ID) r.removeLabelFromResource(common.ResourceTypeRepository, r.repository.RepositoryID, r.label.ID)
} }

View File

@ -16,12 +16,14 @@ package api
import ( import (
"fmt" "fmt"
"net/http"
"strconv"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"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/token" "github.com/goharbor/harbor/src/common/token"
"net/http"
"strconv"
) )
// RobotAPI ... // RobotAPI ...
@ -83,17 +85,24 @@ func (r *RobotAPI) Prepare() {
r.robot = robot r.robot = robot
} }
}
if !(r.Ctx.Input.IsGet() && r.SecurityCtx.HasReadPerm(pid) || func (r *RobotAPI) requireAccess(action rbac.Action) bool {
r.SecurityCtx.HasAllPerm(pid)) { resource := rbac.NewProjectNamespace(r.project.ProjectID).Resource(rbac.ResourceRobot)
if !r.SecurityCtx.Can(action, resource) {
r.HandleForbidden(r.SecurityCtx.GetUsername()) r.HandleForbidden(r.SecurityCtx.GetUsername())
return return false
} }
return true
} }
// Post ... // Post ...
func (r *RobotAPI) Post() { func (r *RobotAPI) Post() {
if !r.requireAccess(rbac.ActionCreate) {
return
}
var robotReq models.RobotReq var robotReq models.RobotReq
r.DecodeJSONReq(&robotReq) r.DecodeJSONReq(&robotReq)
createdName := common.RobotPrefix + robotReq.Name createdName := common.RobotPrefix + robotReq.Name
@ -147,6 +156,10 @@ func (r *RobotAPI) Post() {
// List list all the robots of a project // List list all the robots of a project
func (r *RobotAPI) List() { func (r *RobotAPI) List() {
if !r.requireAccess(rbac.ActionList) {
return
}
query := models.RobotQuery{ query := models.RobotQuery{
ProjectID: r.project.ProjectID, ProjectID: r.project.ProjectID,
} }
@ -171,6 +184,10 @@ func (r *RobotAPI) List() {
// Get get robot by id // Get get robot by id
func (r *RobotAPI) Get() { func (r *RobotAPI) Get() {
if !r.requireAccess(rbac.ActionRead) {
return
}
id, err := r.GetInt64FromPath(":id") id, err := r.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
r.HandleBadRequest(fmt.Sprintf("invalid robot ID: %s", r.GetStringFromPath(":id"))) r.HandleBadRequest(fmt.Sprintf("invalid robot ID: %s", r.GetStringFromPath(":id")))
@ -193,6 +210,10 @@ func (r *RobotAPI) Get() {
// Put disable or enable a robot account // Put disable or enable a robot account
func (r *RobotAPI) Put() { func (r *RobotAPI) Put() {
if !r.requireAccess(rbac.ActionUpdate) {
return
}
var robotReq models.RobotReq var robotReq models.RobotReq
r.DecodeJSONReqAndValidate(&robotReq) r.DecodeJSONReqAndValidate(&robotReq)
r.robot.Disabled = robotReq.Disabled r.robot.Disabled = robotReq.Disabled
@ -206,6 +227,10 @@ func (r *RobotAPI) Put() {
// Delete delete robot by id // Delete delete robot by id
func (r *RobotAPI) Delete() { func (r *RobotAPI) Delete() {
if !r.requireAccess(rbac.ActionDelete) {
return
}
if err := dao.DeleteRobot(r.robot.ID); err != nil { if err := dao.DeleteRobot(r.robot.ID); err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to delete robot %d: %v", r.robot.ID, err)) r.HandleInternalServerError(fmt.Sprintf("failed to delete robot %d: %v", r.robot.ID, err))
return return

View File

@ -17,6 +17,7 @@ package api
import ( import (
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
common_http "github.com/goharbor/harbor/src/common/http" common_http "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/utils" "github.com/goharbor/harbor/src/core/utils"
@ -54,7 +55,9 @@ func (sj *ScanJobAPI) Prepare() {
sj.CustomAbort(http.StatusInternalServerError, "Failed to get Job data") sj.CustomAbort(http.StatusInternalServerError, "Failed to get Job data")
} }
projectName := strings.SplitN(data.Repository, "/", 2)[0] projectName := strings.SplitN(data.Repository, "/", 2)[0]
if !sj.SecurityCtx.HasReadPerm(projectName) {
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagScanJob)
if !sj.SecurityCtx.Can(rbac.ActionRead, resource) {
log.Errorf("User does not have read permission for project: %s", projectName) log.Errorf("User does not have read permission for project: %s", projectName)
sj.HandleForbidden(sj.SecurityCtx.GetUsername()) sj.HandleForbidden(sj.SecurityCtx.GetUsername())
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/docker/distribution/registry/auth/token" "github.com/docker/distribution/registry/auth/token"
"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/security" "github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
@ -158,24 +159,25 @@ func (rep repositoryFilter) filter(ctx security.Context, pm promgr.ProjectManage
if err != nil { if err != nil {
return err return err
} }
project := img.namespace projectName := img.namespace
permission := "" permission := ""
exist, err := pm.Exists(project) exist, err := pm.Exists(projectName)
if err != nil { if err != nil {
return err return err
} }
if !exist { if !exist {
log.Debugf("project %s does not exist, set empty permission", project) log.Debugf("project %s does not exist, set empty permission", projectName)
a.Actions = []string{} a.Actions = []string{}
return nil return nil
} }
if ctx.HasAllPerm(project) { resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepository)
if ctx.Can(rbac.ActionPush, resource) && ctx.Can(rbac.ActionPull, resource) {
permission = "RWM" permission = "RWM"
} else if ctx.HasWritePerm(project) { } else if ctx.Can(rbac.ActionPush, resource) {
permission = "RW" permission = "RW"
} else if ctx.HasReadPerm(project) { } else if ctx.Can(rbac.ActionPull, resource) {
permission = "R" permission = "R"
} }

View File

@ -252,15 +252,6 @@ func (f *fakeSecurityContext) IsSysAdmin() bool {
func (f *fakeSecurityContext) IsSolutionUser() bool { func (f *fakeSecurityContext) IsSolutionUser() bool {
return false return false
} }
func (f *fakeSecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
return false
}
func (f *fakeSecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
return false
}
func (f *fakeSecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
return false
}
func (f *fakeSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool { func (f *fakeSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
return false return false
} }