mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 20:26:13 +01:00
refactor(rbac): refactor rbac impl to improve performance (#9988)
1. Introduce `Evaluator` interface which do the permission checking. 2. `admin`, `lazy`, `rbac`, `namespace` and `evaluartor` set are implemented the `Evaluator` interface. 3. Move project rbac implemention from `project` to `rbac` pkg to reduce the name conflict with project instance of model. 4. Do permission checking in security context by `Evaluator`. 5. Cache the regexp in rbac evaluator for casbin. 6. Cache evaluator in namespace evaluator to improve performance. Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
parent
8ffa79801b
commit
2a243ef7a2
@ -30,6 +30,7 @@ import (
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/robot"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
sca "github.com/goharbor/harbor/src/pkg/scan"
|
||||
@ -522,7 +523,7 @@ func (bc *basicController) makeRobotAccount(projectID int64, repository string)
|
||||
Name: UUID,
|
||||
Description: "for scan",
|
||||
ProjectID: projectID,
|
||||
Access: []*rbac.Policy{{Resource: resource, Action: rbac.ActionScannerPull}},
|
||||
Access: []*types.Policy{{Resource: resource, Action: rbac.ActionScannerPull}},
|
||||
}
|
||||
|
||||
rb, err := bc.rc.CreateRobotAccount(robotReq)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
jm "github.com/goharbor/harbor/src/common/job/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/q"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
sca "github.com/goharbor/harbor/src/pkg/scan"
|
||||
@ -167,8 +168,8 @@ func (suite *ControllerTestSuite) SetupSuite() {
|
||||
rc := &MockRobotController{}
|
||||
|
||||
resource := fmt.Sprintf("/project/%d/repository", suite.artifact.ProjectID)
|
||||
access := []*rbac.Policy{{
|
||||
Resource: rbac.Resource(resource),
|
||||
access := []*types.Policy{{
|
||||
Resource: types.Resource(resource),
|
||||
Action: rbac.ActionScannerPull,
|
||||
}}
|
||||
|
||||
|
@ -1,168 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casbin"
|
||||
"github.com/casbin/casbin/model"
|
||||
"github.com/casbin/casbin/persist"
|
||||
"github.com/casbin/casbin/util"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotImplemented = errors.New("Not implemented")
|
||||
)
|
||||
|
||||
// Syntax for models see https://casbin.org/docs/en/syntax-for-models
|
||||
const modelText = `
|
||||
# Request definition
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
# Policy definition
|
||||
[policy_definition]
|
||||
p = sub, obj, act, eft
|
||||
|
||||
# Role definition
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
# Policy effect
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
||||
|
||||
# Matchers
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && (r.act == p.act || p.act == '*')
|
||||
`
|
||||
|
||||
// keyMatch2 determines whether key1 matches the pattern of key2, its behavior most likely the builtin KeyMatch2
|
||||
// except that the match of ("/project/1/robot", "/project/1") will return false
|
||||
func keyMatch2(key1 string, key2 string) bool {
|
||||
key2 = strings.Replace(key2, "/*", "/.*", -1)
|
||||
|
||||
re := regexp.MustCompile(`(.*):[^/]+(.*)`)
|
||||
for {
|
||||
if !strings.Contains(key2, "/:") {
|
||||
break
|
||||
}
|
||||
|
||||
key2 = re.ReplaceAllString(key2, "$1[^/]+$2")
|
||||
}
|
||||
|
||||
return util.RegexMatch(key1, "^"+key2+"$")
|
||||
}
|
||||
|
||||
func keyMatch2Func(args ...interface{}) (interface{}, error) {
|
||||
name1 := args[0].(string)
|
||||
name2 := args[1].(string)
|
||||
|
||||
return bool(keyMatch2(name1, name2)), nil
|
||||
}
|
||||
|
||||
type userAdapter struct {
|
||||
User
|
||||
}
|
||||
|
||||
func (a *userAdapter) getRolePolicyLines(role Role) []string {
|
||||
lines := []string{}
|
||||
|
||||
roleName := role.GetRoleName()
|
||||
// returns empty policy lines if role name is empty
|
||||
if roleName == "" {
|
||||
return lines
|
||||
}
|
||||
|
||||
for _, policy := range role.GetPolicies() {
|
||||
line := fmt.Sprintf("p, %s, %s, %s, %s", roleName, policy.Resource, policy.Action, policy.GetEffect())
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (a *userAdapter) getUserPolicyLines() []string {
|
||||
lines := []string{}
|
||||
|
||||
username := a.GetUserName()
|
||||
// returns empty policy lines if username is empty
|
||||
if username == "" {
|
||||
return lines
|
||||
}
|
||||
|
||||
for _, policy := range a.GetPolicies() {
|
||||
line := fmt.Sprintf("p, %s, %s, %s, %s", username, policy.Resource, policy.Action, policy.GetEffect())
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (a *userAdapter) getUserAllPolicyLines() []string {
|
||||
lines := []string{}
|
||||
|
||||
username := a.GetUserName()
|
||||
// returns empty policy lines if username is empty
|
||||
if username == "" {
|
||||
return lines
|
||||
}
|
||||
|
||||
lines = append(lines, a.getUserPolicyLines()...)
|
||||
|
||||
for _, role := range a.GetRoles() {
|
||||
lines = append(lines, a.getRolePolicyLines(role)...)
|
||||
lines = append(lines, fmt.Sprintf("g, %s, %s", username, role.GetRoleName()))
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (a *userAdapter) LoadPolicy(model model.Model) error {
|
||||
for _, line := range a.getUserAllPolicyLines() {
|
||||
persist.LoadPolicyLine(line, model)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *userAdapter) SavePolicy(model model.Model) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (a *userAdapter) AddPolicy(sec string, ptype string, rule []string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (a *userAdapter) RemovePolicy(sec string, ptype string, rule []string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (a *userAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func enforcerForUser(user User) *casbin.Enforcer {
|
||||
m := model.Model{}
|
||||
m.LoadModelFromText(modelText)
|
||||
|
||||
e := casbin.NewEnforcer(m, &userAdapter{User: user})
|
||||
e.AddFunction("keyMatch2", keyMatch2Func)
|
||||
return e
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Namespace the namespace interface
|
||||
type Namespace interface {
|
||||
// Kind returns the kind of namespace
|
||||
Kind() string
|
||||
// Resource returns new resource for subresources with the namespace
|
||||
Resource(subresources ...Resource) Resource
|
||||
// Identity returns identity attached with namespace
|
||||
Identity() interface{}
|
||||
// IsPublic returns true if namespace is public
|
||||
IsPublic() bool
|
||||
}
|
||||
|
||||
type projectNamespace struct {
|
||||
projectID int64
|
||||
isPublic bool
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Kind() string {
|
||||
return "project"
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Resource(subresources ...Resource) Resource {
|
||||
return Resource(fmt.Sprintf("/project/%d", ns.projectID)).Subresource(subresources...)
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Identity() interface{} {
|
||||
return ns.projectID
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) IsPublic() bool {
|
||||
return ns.isPublic
|
||||
}
|
||||
|
||||
// NewProjectNamespace returns namespace for project
|
||||
func NewProjectNamespace(projectID int64, isPublic ...bool) Namespace {
|
||||
isPublicNamespace := false
|
||||
if len(isPublic) > 0 {
|
||||
isPublicNamespace = isPublic[0]
|
||||
}
|
||||
return &projectNamespace{projectID: projectID, isPublic: isPublicNamespace}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ProjectNamespaceTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *ProjectNamespaceTestSuite) TestResource() {
|
||||
var namespace Namespace
|
||||
|
||||
namespace = &projectNamespace{projectID: int64(1)}
|
||||
|
||||
suite.Equal(namespace.Resource(Resource("image")), Resource("/project/1/image"))
|
||||
}
|
||||
|
||||
func (suite *ProjectNamespaceTestSuite) TestIdentity() {
|
||||
namespace, _ := Resource("/project/1/image").GetNamespace()
|
||||
suite.Equal(namespace.Identity(), int64(1))
|
||||
}
|
||||
|
||||
func TestProjectNamespaceTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ProjectNamespaceTestSuite))
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
namespaceParsers = map[string]namespaceParser{
|
||||
"project": projectNamespaceParser,
|
||||
}
|
||||
)
|
||||
|
||||
type namespaceParser func(resource Resource) (Namespace, error)
|
||||
|
||||
func projectNamespaceParser(resource Resource) (Namespace, error) {
|
||||
parserRe := regexp.MustCompile("^/project/([^/]*)/?")
|
||||
|
||||
matches := parserRe.FindStringSubmatch(resource.String())
|
||||
|
||||
if len(matches) <= 1 {
|
||||
return nil, errors.New("not support resource")
|
||||
}
|
||||
|
||||
projectID, err := strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &projectNamespace{projectID: projectID}, nil
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
)
|
||||
|
||||
var (
|
||||
// subresource policies for public project
|
||||
publicProjectPolicies = []*rbac.Policy{
|
||||
{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.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.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceArtifactAddition, Action: rbac.ActionRead},
|
||||
}
|
||||
|
||||
// all policies for the projects
|
||||
allPolicies = computeAllPolicies()
|
||||
)
|
||||
|
||||
// PoliciesForPublicProject ...
|
||||
func PoliciesForPublicProject(namespace rbac.Namespace) []*rbac.Policy {
|
||||
policies := []*rbac.Policy{}
|
||||
|
||||
for _, policy := range publicProjectPolicies {
|
||||
policies = append(policies, &rbac.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
||||
|
||||
// GetAllPolicies returns all policies for namespace of the project
|
||||
func GetAllPolicies(namespace rbac.Namespace) []*rbac.Policy {
|
||||
policies := []*rbac.Policy{}
|
||||
|
||||
for _, policy := range allPolicies {
|
||||
policies = append(policies, &rbac.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
||||
|
||||
func computeAllPolicies() []*rbac.Policy {
|
||||
var results []*rbac.Policy
|
||||
|
||||
mp := map[string]bool{}
|
||||
for _, policies := range rolePoliciesMap {
|
||||
for _, policy := range policies {
|
||||
if !mp[policy.String()] {
|
||||
results = append(results, policy)
|
||||
mp[policy.String()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
)
|
||||
|
||||
// visitorContext the context interface for the project visitor
|
||||
type visitorContext interface {
|
||||
IsAuthenticated() bool
|
||||
// GetUsername returns the username of user related to the context
|
||||
GetUsername() string
|
||||
// IsSysAdmin returns whether the user is system admin
|
||||
IsSysAdmin() bool
|
||||
}
|
||||
|
||||
// visitor implement the rbac.User interface for project visitor
|
||||
type visitor struct {
|
||||
ctx visitorContext
|
||||
namespace rbac.Namespace
|
||||
projectRoles []int
|
||||
}
|
||||
|
||||
// GetUserName returns username of the visitor
|
||||
func (v *visitor) GetUserName() string {
|
||||
// anonymous username for unauthenticated Visitor
|
||||
if !v.ctx.IsAuthenticated() {
|
||||
return "anonymous"
|
||||
}
|
||||
|
||||
return v.ctx.GetUsername()
|
||||
}
|
||||
|
||||
// GetPolicies returns policies of the visitor
|
||||
func (v *visitor) GetPolicies() []*rbac.Policy {
|
||||
if v.ctx.IsSysAdmin() {
|
||||
return GetAllPolicies(v.namespace)
|
||||
}
|
||||
|
||||
if v.namespace.IsPublic() {
|
||||
return PoliciesForPublicProject(v.namespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRoles returns roles of the visitor
|
||||
func (v *visitor) GetRoles() []rbac.Role {
|
||||
// Ignore roles when visitor is anonymous or system admin
|
||||
if !v.ctx.IsAuthenticated() || v.ctx.IsSysAdmin() {
|
||||
return nil
|
||||
}
|
||||
|
||||
roles := []rbac.Role{}
|
||||
|
||||
for _, roleID := range v.projectRoles {
|
||||
roles = append(roles, &visitorRole{roleID: roleID, namespace: v.namespace})
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
// NewUser returns rbac.User interface for the project visitor
|
||||
func NewUser(ctx visitorContext, namespace rbac.Namespace, projectRoles ...int) rbac.User {
|
||||
return &visitor{
|
||||
ctx: ctx,
|
||||
namespace: namespace,
|
||||
projectRoles: projectRoles,
|
||||
}
|
||||
}
|
@ -1,435 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
)
|
||||
|
||||
var (
|
||||
rolePoliciesMap = map[string][]*rbac.Policy{
|
||||
"projectAdmin": {
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionDelete},
|
||||
{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.ResourceReplication, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceReplication, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceReplicationJob, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceReplicationJob, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLabelResource, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceQuota, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
|
||||
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionList},
|
||||
|
||||
{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.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagScanJob, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryTagScanJob, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
|
||||
{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.ActionRead}, // download helm chart
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionCreate}, // upload helm chart version
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead}, // read and download helm chart version
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionUpdate},
|
||||
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionCreate},
|
||||
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceArtifactAddition, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTag, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionDelete},
|
||||
},
|
||||
|
||||
"master": {
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
||||
{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.ResourceQuota, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceReplication, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceReplication, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
|
||||
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionList},
|
||||
|
||||
{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.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagScanJob, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryTagScanJob, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceArtifactAddition, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTag, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionDelete},
|
||||
},
|
||||
|
||||
"developer": {
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLog, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceQuota, Action: rbac.ActionRead},
|
||||
|
||||
{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.ActionPush},
|
||||
{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.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceArtifactAddition, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceTag, Action: rbac.ActionCreate},
|
||||
|
||||
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionDelete},
|
||||
},
|
||||
|
||||
"guest": {
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceMember, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLog, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceQuota, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionList},
|
||||
{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.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceArtifactAddition, Action: rbac.ActionRead},
|
||||
},
|
||||
|
||||
"limitedGuest": {
|
||||
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceQuota, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceRepositoryTag, 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.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScan, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceScanner, Action: rbac.ActionRead},
|
||||
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceArtifact, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceArtifactAddition, Action: rbac.ActionRead},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// visitorRole implement the rbac.Role interface
|
||||
type visitorRole struct {
|
||||
namespace rbac.Namespace
|
||||
roleID int
|
||||
}
|
||||
|
||||
// GetRoleName returns role name for the visitor role
|
||||
func (role *visitorRole) GetRoleName() string {
|
||||
switch role.roleID {
|
||||
case common.RoleProjectAdmin:
|
||||
return "projectAdmin"
|
||||
case common.RoleMaster:
|
||||
return "master"
|
||||
case common.RoleDeveloper:
|
||||
return "developer"
|
||||
case common.RoleGuest:
|
||||
return "guest"
|
||||
case common.RoleLimitedGuest:
|
||||
return "limitedGuest"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// GetPolicies returns policies for the visitor role
|
||||
func (role *visitorRole) GetPolicies() []*rbac.Policy {
|
||||
policies := []*rbac.Policy{}
|
||||
|
||||
roleName := role.GetRoleName()
|
||||
if roleName == "" {
|
||||
return policies
|
||||
}
|
||||
|
||||
for _, policy := range rolePoliciesMap[roleName] {
|
||||
policies = append(policies, &rbac.Policy{
|
||||
Resource: role.namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type VisitorRoleTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *VisitorRoleTestSuite) TestGetRoleName() {
|
||||
projectAdmin := visitorRole{roleID: common.RoleProjectAdmin}
|
||||
suite.Equal(projectAdmin.GetRoleName(), "projectAdmin")
|
||||
|
||||
developer := visitorRole{roleID: common.RoleDeveloper}
|
||||
suite.Equal(developer.GetRoleName(), "developer")
|
||||
|
||||
guest := visitorRole{roleID: common.RoleGuest}
|
||||
suite.Equal(guest.GetRoleName(), "guest")
|
||||
|
||||
limitedGuest := visitorRole{roleID: common.RoleLimitedGuest}
|
||||
suite.Equal(limitedGuest.GetRoleName(), "limitedGuest")
|
||||
|
||||
unknow := visitorRole{roleID: 404}
|
||||
suite.Equal(unknow.GetRoleName(), "")
|
||||
}
|
||||
|
||||
func TestVisitorRoleTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(VisitorRoleTestSuite))
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type fakeVisitorContext struct {
|
||||
username string
|
||||
isSysAdmin bool
|
||||
}
|
||||
|
||||
func (ctx *fakeVisitorContext) IsAuthenticated() bool {
|
||||
return ctx.username != ""
|
||||
}
|
||||
|
||||
func (ctx *fakeVisitorContext) GetUsername() string {
|
||||
return ctx.username
|
||||
}
|
||||
|
||||
func (ctx *fakeVisitorContext) IsSysAdmin() bool {
|
||||
return ctx.IsAuthenticated() && ctx.isSysAdmin
|
||||
}
|
||||
|
||||
var (
|
||||
anonymousCtx = &fakeVisitorContext{}
|
||||
authenticatedCtx = &fakeVisitorContext{username: "user"}
|
||||
sysAdminCtx = &fakeVisitorContext{username: "admin", isSysAdmin: true}
|
||||
)
|
||||
|
||||
type VisitorTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *VisitorTestSuite) TestGetPolicies() {
|
||||
namespace := rbac.NewProjectNamespace(1, false)
|
||||
publicNamespace := rbac.NewProjectNamespace(1, true)
|
||||
|
||||
anonymous := NewUser(anonymousCtx, namespace)
|
||||
suite.Nil(anonymous.GetPolicies())
|
||||
|
||||
anonymousForPublicProject := NewUser(anonymousCtx, publicNamespace)
|
||||
suite.Equal(anonymousForPublicProject.GetPolicies(), PoliciesForPublicProject(publicNamespace))
|
||||
|
||||
authenticated := NewUser(authenticatedCtx, namespace)
|
||||
suite.Nil(authenticated.GetPolicies())
|
||||
|
||||
authenticatedForPublicProject := NewUser(authenticatedCtx, publicNamespace)
|
||||
suite.Equal(authenticatedForPublicProject.GetPolicies(), PoliciesForPublicProject(publicNamespace))
|
||||
|
||||
systemAdmin := NewUser(sysAdminCtx, namespace)
|
||||
suite.Equal(systemAdmin.GetPolicies(), GetAllPolicies(namespace))
|
||||
|
||||
systemAdminForPublicProject := NewUser(sysAdminCtx, publicNamespace)
|
||||
suite.Equal(systemAdminForPublicProject.GetPolicies(), GetAllPolicies(publicNamespace))
|
||||
}
|
||||
|
||||
func (suite *VisitorTestSuite) TestGetRoles() {
|
||||
namespace := rbac.NewProjectNamespace(1, false)
|
||||
|
||||
anonymous := NewUser(anonymousCtx, namespace)
|
||||
suite.Nil(anonymous.GetRoles())
|
||||
|
||||
authenticated := NewUser(authenticatedCtx, namespace)
|
||||
suite.Empty(authenticated.GetRoles())
|
||||
|
||||
authenticated = NewUser(authenticatedCtx, namespace, common.RoleProjectAdmin)
|
||||
suite.Len(authenticated.GetRoles(), 1)
|
||||
|
||||
authenticated = NewUser(authenticatedCtx, namespace, common.RoleProjectAdmin, common.RoleDeveloper)
|
||||
suite.Len(authenticated.GetRoles(), 2)
|
||||
}
|
||||
|
||||
func TestVisitorTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(VisitorTestSuite))
|
||||
}
|
81
src/common/rbac/project_evaluator.go
Normal file
81
src/common/rbac/project_evaluator.go
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/security"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator/namespace"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator/rbac"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// NewProjectRBACEvaluator returns permission evaluator for project
|
||||
func NewProjectRBACEvaluator(ctx security.Context, pm promgr.ProjectManager) evaluator.Evaluator {
|
||||
return namespace.New(ProjectNamespaceKind, func(ns types.Namespace) evaluator.Evaluator {
|
||||
project, err := pm.Get(ns.Identity())
|
||||
if err != nil || project == nil {
|
||||
if err != nil {
|
||||
log.Warningf("Failed to get info of project %d for permission evaluator, error: %v", ns.Identity(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.IsAuthenticated() {
|
||||
roles := ctx.GetProjectRoles(project.ProjectID)
|
||||
return rbac.New(NewProjectRBACUser(project, ctx.GetUsername(), roles...))
|
||||
} else if project.IsPublic() {
|
||||
// anonymous access and the project is public
|
||||
return rbac.New(NewProjectRBACUser(project, "anonymous"))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// NewProjectRobotEvaluator returns robot permission evaluator for project
|
||||
func NewProjectRobotEvaluator(ctx security.Context, pm promgr.ProjectManager,
|
||||
robotFactory func(types.Namespace) types.RBACUser) evaluator.Evaluator {
|
||||
|
||||
return namespace.New(ProjectNamespaceKind, func(ns types.Namespace) evaluator.Evaluator {
|
||||
project, err := pm.Get(ns.Identity())
|
||||
if err != nil || project == nil {
|
||||
if err != nil {
|
||||
log.Warningf("Failed to get info of project %d for permission evaluator, error: %v", ns.Identity(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.IsAuthenticated() {
|
||||
evaluators := evaluator.Evaluators{
|
||||
rbac.New(robotFactory(ns)), // robot account access
|
||||
}
|
||||
|
||||
if project.IsPublic() {
|
||||
// authenticated access and the project is public
|
||||
evaluators = evaluators.Add(rbac.New(NewProjectRBACUser(project, ctx.GetUsername())))
|
||||
}
|
||||
|
||||
return evaluators
|
||||
} else if project.IsPublic() {
|
||||
// anonymous access and the project is public
|
||||
return rbac.New(NewProjectRBACUser(project, "anonymous"))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
116
src/common/rbac/project_evaluator_test.go
Normal file
116
src/common/rbac/project_evaluator_test.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
promgr "github.com/goharbor/harbor/src/core/promgr/mocks"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/testing/common/security"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var (
|
||||
projectID = int64(1)
|
||||
|
||||
projectAdminSecurity = makeMockSecurity("projectAdmin", common.RoleProjectAdmin)
|
||||
guestSecurity = makeMockSecurity("guest", common.RoleGuest)
|
||||
anonymousSecurity = makeMockSecurity("")
|
||||
|
||||
publicProjectManager = makeMockProjectManager(projectID, true)
|
||||
privateProjectManager = makeMockProjectManager(projectID, false)
|
||||
)
|
||||
|
||||
func makeMockSecurity(username string, roles ...int) *security.Context {
|
||||
var isAuthenticated bool
|
||||
if username != "" {
|
||||
isAuthenticated = true
|
||||
}
|
||||
|
||||
ctx := &security.Context{}
|
||||
ctx.On("IsAuthenticated").Return(isAuthenticated)
|
||||
ctx.On("GetUsername").Return(username)
|
||||
ctx.On("GetProjectRoles", mock.AnythingOfType("int64")).Return(roles)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func makeMockProjectManager(projectID int64, isPublic bool) *promgr.ProjectManager {
|
||||
pm := &promgr.ProjectManager{}
|
||||
|
||||
project := &models.Project{ProjectID: projectID}
|
||||
if isPublic {
|
||||
project.SetMetadata(models.ProMetaPublic, "true")
|
||||
} else {
|
||||
project.SetMetadata(models.ProMetaPublic, "false")
|
||||
}
|
||||
|
||||
pm.On("Get", projectID).Return(project, nil)
|
||||
|
||||
return pm
|
||||
}
|
||||
|
||||
func makeResource(subresource ...types.Resource) types.Resource {
|
||||
return NewProjectNamespace(projectID).Resource(subresource...)
|
||||
}
|
||||
|
||||
func TestAnonymousAccess(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
evaluator1 := NewProjectRBACEvaluator(anonymousSecurity, publicProjectManager)
|
||||
assert.True(evaluator1.HasPermission(makeResource(ResourceRepository), ActionPull))
|
||||
|
||||
evaluator2 := NewProjectRBACEvaluator(anonymousSecurity, privateProjectManager)
|
||||
assert.False(evaluator2.HasPermission(makeResource(ResourceRepository), ActionPull))
|
||||
|
||||
evaluator3 := NewProjectRobotEvaluator(anonymousSecurity, publicProjectManager, func(ns types.Namespace) types.RBACUser { return nil })
|
||||
assert.True(evaluator3.HasPermission(makeResource(ResourceRepository), ActionPull))
|
||||
|
||||
evaluator4 := NewProjectRobotEvaluator(anonymousSecurity, privateProjectManager, func(ns types.Namespace) types.RBACUser { return nil })
|
||||
assert.False(evaluator4.HasPermission(makeResource(ResourceRepository), ActionPull))
|
||||
}
|
||||
|
||||
func TestProjectRoleAccess(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
evaluator1 := NewProjectRBACEvaluator(projectAdminSecurity, publicProjectManager)
|
||||
assert.True(evaluator1.HasPermission(makeResource(ResourceRepository), ActionPush))
|
||||
|
||||
evaluator2 := NewProjectRBACEvaluator(guestSecurity, publicProjectManager)
|
||||
assert.False(evaluator2.HasPermission(makeResource(ResourceRepository), ActionPush))
|
||||
}
|
||||
|
||||
func BenchmarkProjectRBACEvaluator(b *testing.B) {
|
||||
evaluator := NewProjectRBACEvaluator(projectAdminSecurity, publicProjectManager)
|
||||
resource := NewProjectNamespace(projectID).Resource(ResourceRepository)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
evaluator.HasPermission(resource, ActionPull)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProjectRBACEvaluatorParallel(b *testing.B) {
|
||||
evaluator := NewProjectRBACEvaluator(projectAdminSecurity, publicProjectManager)
|
||||
resource := NewProjectNamespace(projectID).Resource(ResourceRepository)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
evaluator.HasPermission(resource, ActionPull)
|
||||
}
|
||||
})
|
||||
}
|
77
src/common/rbac/project_namespace.go
Normal file
77
src/common/rbac/project_namespace.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProjectNamespaceKind kind for project namespace
|
||||
ProjectNamespaceKind = "project"
|
||||
)
|
||||
|
||||
var (
|
||||
projectNamespaceRe = regexp.MustCompile("^/project/([^/]*)/?")
|
||||
)
|
||||
|
||||
type projectNamespace struct {
|
||||
projectID int64
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Kind() string {
|
||||
return ProjectNamespaceKind
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Resource(subresources ...types.Resource) types.Resource {
|
||||
return types.Resource(fmt.Sprintf("/project/%d", ns.projectID)).Subresource(subresources...)
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Identity() interface{} {
|
||||
return ns.projectID
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) GetPolicies() []*types.Policy {
|
||||
return GetPoliciesOfProject(ns.projectID)
|
||||
}
|
||||
|
||||
// NewProjectNamespace returns namespace for project
|
||||
func NewProjectNamespace(projectID int64) types.Namespace {
|
||||
return &projectNamespace{projectID: projectID}
|
||||
}
|
||||
|
||||
// ProjectNamespaceParse ...
|
||||
func ProjectNamespaceParse(resource types.Resource) (types.Namespace, bool) {
|
||||
matches := projectNamespaceRe.FindStringSubmatch(resource.String())
|
||||
|
||||
if len(matches) <= 1 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
projectID, err := strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return NewProjectNamespace(projectID), true
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.RegistryNamespaceParse(ProjectNamespaceKind, ProjectNamespaceParse)
|
||||
}
|
436
src/common/rbac/project_rbac_role.go
Normal file
436
src/common/rbac/project_rbac_role.go
Normal file
@ -0,0 +1,436 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
var (
|
||||
rolePoliciesMap = map[string][]*types.Policy{
|
||||
"projectAdmin": {
|
||||
{Resource: ResourceSelf, Action: ActionRead},
|
||||
{Resource: ResourceSelf, Action: ActionUpdate},
|
||||
{Resource: ResourceSelf, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceMember, Action: ActionCreate},
|
||||
{Resource: ResourceMember, Action: ActionRead},
|
||||
{Resource: ResourceMember, Action: ActionUpdate},
|
||||
{Resource: ResourceMember, Action: ActionDelete},
|
||||
{Resource: ResourceMember, Action: ActionList},
|
||||
|
||||
{Resource: ResourceMetadata, Action: ActionCreate},
|
||||
{Resource: ResourceMetadata, Action: ActionRead},
|
||||
{Resource: ResourceMetadata, Action: ActionUpdate},
|
||||
{Resource: ResourceMetadata, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceLog, Action: ActionList},
|
||||
|
||||
{Resource: ResourceReplication, Action: ActionRead},
|
||||
{Resource: ResourceReplication, Action: ActionList},
|
||||
|
||||
{Resource: ResourceReplicationJob, Action: ActionRead},
|
||||
{Resource: ResourceReplicationJob, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLabel, Action: ActionCreate},
|
||||
{Resource: ResourceLabel, Action: ActionRead},
|
||||
{Resource: ResourceLabel, Action: ActionUpdate},
|
||||
{Resource: ResourceLabel, Action: ActionDelete},
|
||||
{Resource: ResourceLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLabelResource, Action: ActionList},
|
||||
|
||||
{Resource: ResourceQuota, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepository, Action: ActionCreate},
|
||||
{Resource: ResourceRepository, Action: ActionRead},
|
||||
{Resource: ResourceRepository, Action: ActionUpdate},
|
||||
{Resource: ResourceRepository, Action: ActionDelete},
|
||||
{Resource: ResourceRepository, Action: ActionList},
|
||||
{Resource: ResourceRepository, Action: ActionPull},
|
||||
{Resource: ResourceRepository, Action: ActionPush},
|
||||
|
||||
{Resource: ResourceTagRetention, Action: ActionCreate},
|
||||
{Resource: ResourceTagRetention, Action: ActionRead},
|
||||
{Resource: ResourceTagRetention, Action: ActionUpdate},
|
||||
{Resource: ResourceTagRetention, Action: ActionDelete},
|
||||
{Resource: ResourceTagRetention, Action: ActionList},
|
||||
{Resource: ResourceTagRetention, Action: ActionOperate},
|
||||
|
||||
{Resource: ResourceImmutableTag, Action: ActionCreate},
|
||||
{Resource: ResourceImmutableTag, Action: ActionUpdate},
|
||||
{Resource: ResourceImmutableTag, Action: ActionDelete},
|
||||
{Resource: ResourceImmutableTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTag, Action: ActionRead},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagScanJob, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryTagScanJob, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepositoryTagVulnerability, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagManifest, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChart, Action: ActionCreate}, // upload helm chart
|
||||
{Resource: ResourceHelmChart, Action: ActionRead}, // download helm chart
|
||||
{Resource: ResourceHelmChart, Action: ActionDelete},
|
||||
{Resource: ResourceHelmChart, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionCreate}, // upload helm chart version
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionRead}, // read and download helm chart version
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionDelete},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersionLabel, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChartVersionLabel, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceConfiguration, Action: ActionRead},
|
||||
{Resource: ResourceConfiguration, Action: ActionUpdate},
|
||||
|
||||
{Resource: ResourceRobot, Action: ActionCreate},
|
||||
{Resource: ResourceRobot, Action: ActionRead},
|
||||
{Resource: ResourceRobot, Action: ActionUpdate},
|
||||
{Resource: ResourceRobot, Action: ActionDelete},
|
||||
{Resource: ResourceRobot, Action: ActionList},
|
||||
|
||||
{Resource: ResourceNotificationPolicy, Action: ActionCreate},
|
||||
{Resource: ResourceNotificationPolicy, Action: ActionUpdate},
|
||||
{Resource: ResourceNotificationPolicy, Action: ActionDelete},
|
||||
{Resource: ResourceNotificationPolicy, Action: ActionList},
|
||||
{Resource: ResourceNotificationPolicy, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScan, Action: ActionCreate},
|
||||
{Resource: ResourceScan, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScanner, Action: ActionRead},
|
||||
{Resource: ResourceScanner, Action: ActionCreate},
|
||||
|
||||
{Resource: ResourceArtifact, Action: ActionCreate},
|
||||
{Resource: ResourceArtifact, Action: ActionRead},
|
||||
{Resource: ResourceArtifact, Action: ActionDelete},
|
||||
{Resource: ResourceArtifact, Action: ActionList},
|
||||
{Resource: ResourceArtifactAddition, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceTag, Action: ActionCreate},
|
||||
{Resource: ResourceTag, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceArtifactLabel, Action: ActionCreate},
|
||||
{Resource: ResourceArtifactLabel, Action: ActionDelete},
|
||||
},
|
||||
|
||||
"master": {
|
||||
{Resource: ResourceSelf, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceMember, Action: ActionRead},
|
||||
{Resource: ResourceMember, Action: ActionList},
|
||||
|
||||
{Resource: ResourceMetadata, Action: ActionCreate},
|
||||
{Resource: ResourceMetadata, Action: ActionRead},
|
||||
{Resource: ResourceMetadata, Action: ActionUpdate},
|
||||
{Resource: ResourceMetadata, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceLog, Action: ActionList},
|
||||
|
||||
{Resource: ResourceQuota, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceReplication, Action: ActionRead},
|
||||
{Resource: ResourceReplication, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLabel, Action: ActionCreate},
|
||||
{Resource: ResourceLabel, Action: ActionRead},
|
||||
{Resource: ResourceLabel, Action: ActionUpdate},
|
||||
{Resource: ResourceLabel, Action: ActionDelete},
|
||||
{Resource: ResourceLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepository, Action: ActionCreate},
|
||||
{Resource: ResourceRepository, Action: ActionRead},
|
||||
{Resource: ResourceRepository, Action: ActionUpdate},
|
||||
{Resource: ResourceRepository, Action: ActionDelete},
|
||||
{Resource: ResourceRepository, Action: ActionList},
|
||||
{Resource: ResourceRepository, Action: ActionPush},
|
||||
{Resource: ResourceRepository, Action: ActionPull},
|
||||
|
||||
{Resource: ResourceTagRetention, Action: ActionCreate},
|
||||
{Resource: ResourceTagRetention, Action: ActionRead},
|
||||
{Resource: ResourceTagRetention, Action: ActionUpdate},
|
||||
{Resource: ResourceTagRetention, Action: ActionDelete},
|
||||
{Resource: ResourceTagRetention, Action: ActionList},
|
||||
{Resource: ResourceTagRetention, Action: ActionOperate},
|
||||
|
||||
{Resource: ResourceImmutableTag, Action: ActionCreate},
|
||||
{Resource: ResourceImmutableTag, Action: ActionUpdate},
|
||||
{Resource: ResourceImmutableTag, Action: ActionDelete},
|
||||
{Resource: ResourceImmutableTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTag, Action: ActionRead},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagScanJob, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryTagScanJob, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepositoryTagVulnerability, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagManifest, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChart, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChart, Action: ActionRead},
|
||||
{Resource: ResourceHelmChart, Action: ActionDelete},
|
||||
{Resource: ResourceHelmChart, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionRead},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionDelete},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersionLabel, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChartVersionLabel, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceConfiguration, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRobot, Action: ActionRead},
|
||||
{Resource: ResourceRobot, Action: ActionList},
|
||||
|
||||
{Resource: ResourceNotificationPolicy, Action: ActionList},
|
||||
|
||||
{Resource: ResourceScan, Action: ActionCreate},
|
||||
{Resource: ResourceScan, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScanner, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceArtifact, Action: ActionCreate},
|
||||
{Resource: ResourceArtifact, Action: ActionRead},
|
||||
{Resource: ResourceArtifact, Action: ActionDelete},
|
||||
{Resource: ResourceArtifact, Action: ActionList},
|
||||
{Resource: ResourceArtifactAddition, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceTag, Action: ActionCreate},
|
||||
{Resource: ResourceTag, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceArtifactLabel, Action: ActionCreate},
|
||||
{Resource: ResourceArtifactLabel, Action: ActionDelete},
|
||||
},
|
||||
|
||||
"developer": {
|
||||
{Resource: ResourceSelf, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceMember, Action: ActionRead},
|
||||
{Resource: ResourceMember, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLog, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLabel, Action: ActionRead},
|
||||
{Resource: ResourceLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceQuota, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepository, Action: ActionCreate},
|
||||
{Resource: ResourceRepository, Action: ActionRead},
|
||||
{Resource: ResourceRepository, Action: ActionUpdate},
|
||||
{Resource: ResourceRepository, Action: ActionList},
|
||||
{Resource: ResourceRepository, Action: ActionPush},
|
||||
{Resource: ResourceRepository, Action: ActionPull},
|
||||
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTag, Action: ActionRead},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagVulnerability, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagManifest, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionCreate},
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionDelete},
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChart, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChart, Action: ActionRead},
|
||||
{Resource: ResourceHelmChart, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionRead},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersionLabel, Action: ActionCreate},
|
||||
{Resource: ResourceHelmChartVersionLabel, Action: ActionDelete},
|
||||
|
||||
{Resource: ResourceConfiguration, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRobot, Action: ActionRead},
|
||||
{Resource: ResourceRobot, Action: ActionList},
|
||||
|
||||
{Resource: ResourceScan, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScanner, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceArtifact, Action: ActionCreate},
|
||||
{Resource: ResourceArtifact, Action: ActionRead},
|
||||
{Resource: ResourceArtifact, Action: ActionList},
|
||||
{Resource: ResourceArtifactAddition, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceTag, Action: ActionCreate},
|
||||
|
||||
{Resource: ResourceArtifactLabel, Action: ActionCreate},
|
||||
{Resource: ResourceArtifactLabel, Action: ActionDelete},
|
||||
},
|
||||
|
||||
"guest": {
|
||||
{Resource: ResourceSelf, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceMember, Action: ActionRead},
|
||||
{Resource: ResourceMember, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLog, Action: ActionList},
|
||||
|
||||
{Resource: ResourceLabel, Action: ActionRead},
|
||||
{Resource: ResourceLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceQuota, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepository, Action: ActionRead},
|
||||
{Resource: ResourceRepository, Action: ActionList},
|
||||
{Resource: ResourceRepository, Action: ActionPull},
|
||||
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTag, Action: ActionRead},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagVulnerability, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagManifest, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceHelmChart, Action: ActionRead},
|
||||
{Resource: ResourceHelmChart, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionRead},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionList},
|
||||
|
||||
{Resource: ResourceConfiguration, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRobot, Action: ActionRead},
|
||||
{Resource: ResourceRobot, Action: ActionList},
|
||||
|
||||
{Resource: ResourceScan, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScanner, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceArtifact, Action: ActionRead},
|
||||
{Resource: ResourceArtifact, Action: ActionList},
|
||||
{Resource: ResourceArtifactAddition, Action: ActionRead},
|
||||
},
|
||||
|
||||
"limitedGuest": {
|
||||
{Resource: ResourceSelf, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceQuota, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceRepository, Action: ActionList},
|
||||
{Resource: ResourceRepository, Action: ActionPull},
|
||||
|
||||
{Resource: ResourceRepositoryTag, Action: ActionRead},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagVulnerability, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagManifest, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceHelmChart, Action: ActionRead},
|
||||
{Resource: ResourceHelmChart, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionRead},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionList},
|
||||
|
||||
{Resource: ResourceConfiguration, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScan, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceScanner, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceArtifact, Action: ActionRead},
|
||||
{Resource: ResourceArtifact, Action: ActionList},
|
||||
{Resource: ResourceArtifactAddition, Action: ActionRead},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// projectRBACRole implement the RBACRole interface
|
||||
type projectRBACRole struct {
|
||||
projectID int64
|
||||
roleID int
|
||||
}
|
||||
|
||||
// GetRoleName returns role name for the visitor role
|
||||
func (role *projectRBACRole) GetRoleName() string {
|
||||
switch role.roleID {
|
||||
case common.RoleProjectAdmin:
|
||||
return "projectAdmin"
|
||||
case common.RoleMaster:
|
||||
return "master"
|
||||
case common.RoleDeveloper:
|
||||
return "developer"
|
||||
case common.RoleGuest:
|
||||
return "guest"
|
||||
case common.RoleLimitedGuest:
|
||||
return "limitedGuest"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// GetPolicies returns policies for the visitor role
|
||||
func (role *projectRBACRole) GetPolicies() []*types.Policy {
|
||||
policies := []*types.Policy{}
|
||||
|
||||
roleName := role.GetRoleName()
|
||||
if roleName == "" {
|
||||
return policies
|
||||
}
|
||||
|
||||
namespace := NewProjectNamespace(role.projectID)
|
||||
for _, policy := range rolePoliciesMap[roleName] {
|
||||
policies = append(policies, &types.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
59
src/common/rbac/project_rbac_user.go
Normal file
59
src/common/rbac/project_rbac_user.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
type projectRBACUser struct {
|
||||
project *models.Project
|
||||
username string
|
||||
projectRoles []int
|
||||
}
|
||||
|
||||
// GetUserName returns username of the visitor
|
||||
func (user *projectRBACUser) GetUserName() string {
|
||||
return user.username
|
||||
}
|
||||
|
||||
// GetPolicies returns policies of the visitor
|
||||
func (user *projectRBACUser) GetPolicies() []*types.Policy {
|
||||
if user.project.IsPublic() {
|
||||
return getPoliciesForPublicProject(user.project.ProjectID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRoles returns roles of the visitor
|
||||
func (user *projectRBACUser) GetRoles() []types.RBACRole {
|
||||
roles := []types.RBACRole{}
|
||||
for _, roleID := range user.projectRoles {
|
||||
roles = append(roles, &projectRBACRole{projectID: user.project.ProjectID, roleID: roleID})
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
// NewProjectRBACUser returns RBACUser for the project
|
||||
func NewProjectRBACUser(project *models.Project, username string, projectRoles ...int) types.RBACUser {
|
||||
return &projectRBACUser{
|
||||
project: project,
|
||||
username: username,
|
||||
projectRoles: projectRoles,
|
||||
}
|
||||
}
|
106
src/common/rbac/project_rbac_util.go
Normal file
106
src/common/rbac/project_rbac_util.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// subresource policies for public project
|
||||
publicProjectPolicies = []*types.Policy{
|
||||
{Resource: ResourceSelf, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceLabel, Action: ActionRead},
|
||||
{Resource: ResourceLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepository, Action: ActionList},
|
||||
{Resource: ResourceRepository, Action: ActionPull},
|
||||
|
||||
{Resource: ResourceRepositoryLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTag, Action: ActionRead},
|
||||
{Resource: ResourceRepositoryTag, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagLabel, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagVulnerability, Action: ActionList},
|
||||
|
||||
{Resource: ResourceRepositoryTagManifest, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceHelmChart, Action: ActionRead},
|
||||
{Resource: ResourceHelmChart, Action: ActionList},
|
||||
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionRead},
|
||||
{Resource: ResourceHelmChartVersion, Action: ActionList},
|
||||
|
||||
{Resource: ResourceScan, Action: ActionRead},
|
||||
{Resource: ResourceScanner, Action: ActionRead},
|
||||
|
||||
{Resource: ResourceArtifact, Action: ActionRead},
|
||||
{Resource: ResourceArtifact, Action: ActionList},
|
||||
{Resource: ResourceArtifactAddition, Action: ActionRead},
|
||||
}
|
||||
|
||||
// sub policies for the projects
|
||||
subPoliciesForProject = computeSubPoliciesForProject()
|
||||
)
|
||||
|
||||
func getPoliciesForPublicProject(projectID int64) []*types.Policy {
|
||||
policies := []*types.Policy{}
|
||||
|
||||
namespace := NewProjectNamespace(projectID)
|
||||
for _, policy := range publicProjectPolicies {
|
||||
policies = append(policies, &types.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
||||
|
||||
// GetPoliciesOfProject returns all policies for namespace of the project
|
||||
func GetPoliciesOfProject(projectID int64) []*types.Policy {
|
||||
policies := []*types.Policy{}
|
||||
|
||||
namespace := NewProjectNamespace(projectID)
|
||||
for _, policy := range subPoliciesForProject {
|
||||
policies = append(policies, &types.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
||||
|
||||
func computeSubPoliciesForProject() []*types.Policy {
|
||||
var results []*types.Policy
|
||||
|
||||
mp := map[string]bool{}
|
||||
for _, policies := range rolePoliciesMap {
|
||||
for _, policy := range policies {
|
||||
if !mp[policy.String()] {
|
||||
results = append(results, policy)
|
||||
mp[policy.String()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
@ -15,139 +15,11 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// EffectAllow allow effect
|
||||
EffectAllow = Effect("allow")
|
||||
// EffectDeny deny effect
|
||||
EffectDeny = Effect("deny")
|
||||
)
|
||||
// Resource alias type for types.Resource
|
||||
type Resource = types.Resource
|
||||
|
||||
// Resource the type of resource
|
||||
type Resource string
|
||||
|
||||
// RelativeTo returns relative resource to other resource
|
||||
func (res Resource) RelativeTo(other Resource) (Resource, error) {
|
||||
prefix := other.String()
|
||||
str := res.String()
|
||||
|
||||
if !strings.HasPrefix(str, prefix) {
|
||||
return Resource(""), errors.New("value error")
|
||||
}
|
||||
|
||||
relative := strings.TrimPrefix(str, prefix)
|
||||
if strings.HasPrefix(relative, "/") {
|
||||
relative = relative[1:]
|
||||
}
|
||||
|
||||
if relative == "" {
|
||||
relative = "."
|
||||
}
|
||||
|
||||
return Resource(relative), nil
|
||||
}
|
||||
|
||||
func (res Resource) String() string {
|
||||
return string(res)
|
||||
}
|
||||
|
||||
// Subresource returns subresource
|
||||
func (res Resource) Subresource(resources ...Resource) Resource {
|
||||
elements := []string{res.String()}
|
||||
|
||||
for _, resource := range resources {
|
||||
elements = append(elements, resource.String())
|
||||
}
|
||||
|
||||
return Resource(path.Join(elements...))
|
||||
}
|
||||
|
||||
// GetNamespace returns namespace from resource
|
||||
func (res Resource) GetNamespace() (Namespace, error) {
|
||||
for _, parser := range namespaceParsers {
|
||||
namespace, err := parser(res)
|
||||
if err == nil {
|
||||
return namespace, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no namespace found for %s", res)
|
||||
}
|
||||
|
||||
// Action the type of action
|
||||
type Action string
|
||||
|
||||
func (act Action) String() string {
|
||||
return string(act)
|
||||
}
|
||||
|
||||
// Effect the type of effect
|
||||
type Effect string
|
||||
|
||||
func (eff Effect) String() string {
|
||||
return string(eff)
|
||||
}
|
||||
|
||||
// Policy the type of policy
|
||||
type Policy struct {
|
||||
Resource
|
||||
Action
|
||||
Effect
|
||||
}
|
||||
|
||||
// GetEffect returns effect of resource, default is allow
|
||||
func (p *Policy) GetEffect() string {
|
||||
eft := p.Effect
|
||||
if eft == "" {
|
||||
eft = EffectAllow
|
||||
}
|
||||
|
||||
return eft.String()
|
||||
}
|
||||
|
||||
func (p *Policy) String() string {
|
||||
return p.Resource.String() + ":" + p.Action.String() + ":" + p.GetEffect()
|
||||
}
|
||||
|
||||
// Role the interface of rbac role
|
||||
type Role interface {
|
||||
// GetRoleName returns the role identity, if empty string role's policies will be ignore
|
||||
GetRoleName() string
|
||||
GetPolicies() []*Policy
|
||||
}
|
||||
|
||||
// User the interface of rbac user
|
||||
type User interface {
|
||||
// GetUserName returns the user identity, if empty string user's all policies will be ignore
|
||||
GetUserName() string
|
||||
GetPolicies() []*Policy
|
||||
GetRoles() []Role
|
||||
}
|
||||
|
||||
// BaseUser the type implement User interface whose policies are empty
|
||||
type BaseUser struct{}
|
||||
|
||||
// GetRoles returns roles of the user
|
||||
func (u *BaseUser) GetRoles() []Role {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserName returns user identity
|
||||
func (u *BaseUser) GetUserName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetPolicies returns policies of the user
|
||||
func (u *BaseUser) GetPolicies() []*Policy {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasPermission returns whether the user has action permission on resource
|
||||
func HasPermission(user User, resource Resource, action Action) bool {
|
||||
return enforcerForUser(user).Enforce(user.GetUserName(), resource.String(), action.String())
|
||||
}
|
||||
// Action alias type for types.Action
|
||||
type Action = types.Action
|
||||
|
@ -1,439 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type role struct {
|
||||
RoleName string
|
||||
}
|
||||
|
||||
func (r *role) GetRoleName() string {
|
||||
return r.RoleName
|
||||
}
|
||||
|
||||
func (r *role) GetPolicies() []*Policy {
|
||||
return []*Policy{
|
||||
{Resource: "/project", Action: "create"},
|
||||
{Resource: "/project", Action: "update"},
|
||||
}
|
||||
}
|
||||
|
||||
type userWithRoles struct {
|
||||
Username string
|
||||
RoleName string
|
||||
BaseUser
|
||||
}
|
||||
|
||||
func (u *userWithRoles) GetUserName() string {
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func (u *userWithRoles) GetRoles() []Role {
|
||||
return []Role{
|
||||
&role{RoleName: u.RoleName},
|
||||
}
|
||||
}
|
||||
|
||||
type userWithoutRoles struct {
|
||||
Username string
|
||||
UserPolicies []*Policy
|
||||
BaseUser
|
||||
}
|
||||
|
||||
func (u *userWithoutRoles) GetUserName() string {
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func (u *userWithoutRoles) GetPolicies() []*Policy {
|
||||
return u.UserPolicies
|
||||
}
|
||||
|
||||
func TestHasPermissionUserWithRoles(t *testing.T) {
|
||||
type args struct {
|
||||
user User
|
||||
resource Resource
|
||||
action Action
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "project create for project admin",
|
||||
args: args{
|
||||
&userWithRoles{Username: "project admin", RoleName: "projectAdmin"},
|
||||
"/project",
|
||||
"create",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "project update for project admin",
|
||||
args: args{
|
||||
&userWithRoles{Username: "project admin", RoleName: "projectAdmin"},
|
||||
"/project",
|
||||
"update",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "project delete for project admin",
|
||||
args: args{
|
||||
&userWithRoles{Username: "project admin", RoleName: "projectAdmin"},
|
||||
"/project",
|
||||
"delete",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HasPermission(tt.args.user, tt.args.resource, tt.args.action); got != tt.want {
|
||||
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPermissionUserWithoutRoles(t *testing.T) {
|
||||
|
||||
type args struct {
|
||||
user User
|
||||
resource Resource
|
||||
action Action
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "project create for user without roles",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project", Action: "create"}}},
|
||||
"/project",
|
||||
"create",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "project delete test for user without roles",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project", Action: "create"}}},
|
||||
"/project",
|
||||
"delete",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HasPermission(tt.args.user, tt.args.resource, tt.args.action); got != tt.want {
|
||||
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPermissionUsernameEmpty(t *testing.T) {
|
||||
|
||||
type args struct {
|
||||
user User
|
||||
resource Resource
|
||||
action Action
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "project create for user without roles",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "", UserPolicies: []*Policy{{Resource: "/project", Action: "create"}}},
|
||||
"/project",
|
||||
"create",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HasPermission(tt.args.user, tt.args.resource, tt.args.action); got != tt.want {
|
||||
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPermissionRoleNameEmpty(t *testing.T) {
|
||||
type args struct {
|
||||
user User
|
||||
resource Resource
|
||||
action Action
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "project create for project admin",
|
||||
args: args{
|
||||
&userWithRoles{Username: "project admin", RoleName: ""},
|
||||
"/project",
|
||||
"create",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "project update for project admin",
|
||||
args: args{
|
||||
&userWithRoles{Username: "project admin", RoleName: ""},
|
||||
"/project",
|
||||
"update",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HasPermission(tt.args.user, tt.args.resource, tt.args.action); got != tt.want {
|
||||
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPermissionResourceKeyMatch(t *testing.T) {
|
||||
|
||||
type args struct {
|
||||
user User
|
||||
resource Resource
|
||||
action Action
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "project member create for resource key match",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project/1/*", Action: "*"}}},
|
||||
"/project/1/member",
|
||||
"create",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "project member create for resource key match",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project/:id/*", Action: "*"}}},
|
||||
"/project/1/member",
|
||||
"create",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "project repository create test for resource key match",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project/1/*", Action: "create"}}},
|
||||
"/project/1/repository",
|
||||
"create",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "project repository delete test for resource key match",
|
||||
args: args{
|
||||
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project/1/*", Action: "create"}}},
|
||||
"/project/1/repository",
|
||||
"delete",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HasPermission(tt.args.user, tt.args.resource, tt.args.action); got != tt.want {
|
||||
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPermissionPolicyDeny(t *testing.T) {
|
||||
|
||||
type args struct {
|
||||
user User
|
||||
resource Resource
|
||||
action Action
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "project member create for resource deny",
|
||||
args: args{
|
||||
&userWithoutRoles{
|
||||
Username: "user1",
|
||||
UserPolicies: []*Policy{
|
||||
{Resource: "/project/1/*", Action: "*"},
|
||||
{Resource: "/project/1/member", Action: "create", Effect: EffectDeny},
|
||||
},
|
||||
},
|
||||
"/project/1/member",
|
||||
"create",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HasPermission(tt.args.user, tt.args.resource, tt.args.action); got != tt.want {
|
||||
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResource_Subresource(t *testing.T) {
|
||||
type args struct {
|
||||
resources []Resource
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
res Resource
|
||||
args args
|
||||
want Resource
|
||||
}{
|
||||
{
|
||||
name: "subresource image",
|
||||
res: Resource("/project/1"),
|
||||
args: args{
|
||||
resources: []Resource{"image"},
|
||||
},
|
||||
want: Resource("/project/1/image"),
|
||||
},
|
||||
{
|
||||
name: "subresource image build-history",
|
||||
res: Resource("/project/1"),
|
||||
args: args{
|
||||
resources: []Resource{"image", "12", "build-history"},
|
||||
},
|
||||
want: Resource("/project/1/image/12/build-history"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.res.Subresource(tt.args.resources...); got != tt.want {
|
||||
t.Errorf("Resource.Subresource() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResource_GetNamespace(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
res Resource
|
||||
want Namespace
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "project namespace",
|
||||
res: Resource("/project/1"),
|
||||
want: &projectNamespace{int64(1), false},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unknow namespace",
|
||||
res: Resource("/unknow/1"),
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.res.GetNamespace()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Resource.GetNamespace() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Resource.GetNamespace() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResource_RelativeTo(t *testing.T) {
|
||||
type args struct {
|
||||
other Resource
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
res Resource
|
||||
args args
|
||||
want Resource
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "/project/1/image",
|
||||
res: Resource("/project/1/image"),
|
||||
args: args{other: Resource("/project/1")},
|
||||
want: Resource("image"),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "/project/1",
|
||||
res: Resource("/project/1"),
|
||||
args: args{other: Resource("/project/1")},
|
||||
want: Resource("."),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "/project/1",
|
||||
res: Resource("/project/1"),
|
||||
args: args{other: Resource("/system")},
|
||||
want: Resource(""),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.res.RelativeTo(tt.args.other)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Resource.RelativeTo() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("Resource.RelativeTo() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// Context abstracts the operations related with authN and authZ
|
||||
@ -38,7 +38,7 @@ type Context interface {
|
||||
// Get user's role in provided project
|
||||
GetProjectRoles(projectIDOrName interface{}) []int
|
||||
// Can returns whether the user can do action on resource
|
||||
Can(action rbac.Action, resource rbac.Resource) bool
|
||||
Can(action types.Action, resource types.Resource) bool
|
||||
}
|
||||
|
||||
type securityKey struct{}
|
||||
|
@ -15,19 +15,25 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/rbac/project"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator/admin"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// SecurityContext implements security.Context interface based on database
|
||||
type SecurityContext struct {
|
||||
user *models.User
|
||||
pm promgr.ProjectManager
|
||||
user *models.User
|
||||
pm promgr.ProjectManager
|
||||
evaluator evaluator.Evaluator
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// NewSecurityContext ...
|
||||
@ -77,20 +83,18 @@ func (s *SecurityContext) IsSolutionUser() bool {
|
||||
}
|
||||
|
||||
// Can returns whether the user can do action on resource
|
||||
func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
|
||||
ns, err := resource.GetNamespace()
|
||||
if err == nil {
|
||||
switch ns.Kind() {
|
||||
case "project":
|
||||
projectID := ns.Identity().(int64)
|
||||
isPublicProject, _ := s.pm.IsPublic(projectID)
|
||||
projectNamespace := rbac.NewProjectNamespace(projectID, isPublicProject)
|
||||
user := project.NewUser(s, projectNamespace, s.GetProjectRoles(projectID)...)
|
||||
return rbac.HasPermission(user, resource, action)
|
||||
func (s *SecurityContext) Can(action types.Action, resource types.Resource) bool {
|
||||
s.once.Do(func() {
|
||||
var evaluators evaluator.Evaluators
|
||||
if s.IsSysAdmin() {
|
||||
evaluators = evaluators.Add(admin.New(s.GetUsername()))
|
||||
}
|
||||
}
|
||||
evaluators = evaluators.Add(rbac.NewProjectRBACEvaluator(s, s.pm))
|
||||
|
||||
return false
|
||||
s.evaluator = evaluators
|
||||
})
|
||||
|
||||
return s.evaluator != nil && s.evaluator.HasPermission(resource, action)
|
||||
}
|
||||
|
||||
// GetProjectRoles ...
|
||||
|
@ -15,21 +15,27 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
)
|
||||
|
||||
// SecurityContext implements security.Context interface based on database
|
||||
type SecurityContext struct {
|
||||
robot *model.Robot
|
||||
pm promgr.ProjectManager
|
||||
policy []*rbac.Policy
|
||||
robot *model.Robot
|
||||
pm promgr.ProjectManager
|
||||
policy []*types.Policy
|
||||
evaluator evaluator.Evaluator
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// NewSecurityContext ...
|
||||
func NewSecurityContext(robot *model.Robot, pm promgr.ProjectManager, policy []*rbac.Policy) *SecurityContext {
|
||||
func NewSecurityContext(robot *model.Robot, pm promgr.ProjectManager, policy []*types.Policy) *SecurityContext {
|
||||
return &SecurityContext{
|
||||
robot: robot,
|
||||
pm: pm,
|
||||
@ -77,18 +83,14 @@ func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
|
||||
}
|
||||
|
||||
// Can returns whether the robot can do action on resource
|
||||
func (s *SecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
|
||||
ns, err := resource.GetNamespace()
|
||||
if err == nil {
|
||||
switch ns.Kind() {
|
||||
case "project":
|
||||
projectID := ns.Identity().(int64)
|
||||
isPublicProject, _ := s.pm.IsPublic(projectID)
|
||||
projectNamespace := rbac.NewProjectNamespace(projectID, isPublicProject)
|
||||
robot := NewRobot(s.GetUsername(), projectNamespace, s.policy)
|
||||
return rbac.HasPermission(robot, resource, action)
|
||||
func (s *SecurityContext) Can(action types.Action, resource types.Resource) bool {
|
||||
s.once.Do(func() {
|
||||
robotFactory := func(ns types.Namespace) types.RBACUser {
|
||||
return NewRobot(s.GetUsername(), ns, s.policy)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
s.evaluator = rbac.NewProjectRobotEvaluator(s, s.pm, robotFactory)
|
||||
})
|
||||
|
||||
return s.evaluator != nil && s.evaluator.HasPermission(resource, action)
|
||||
}
|
||||
|
@ -17,15 +17,16 @@ package robot
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"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/test"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
"github.com/goharbor/harbor/src/core/promgr/pmsdriver/local"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -40,45 +41,7 @@ var (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dbHost := os.Getenv("POSTGRESQL_HOST")
|
||||
if len(dbHost) == 0 {
|
||||
log.Fatalf("environment variable POSTGRES_HOST is not set")
|
||||
}
|
||||
dbUser := os.Getenv("POSTGRESQL_USR")
|
||||
if len(dbUser) == 0 {
|
||||
log.Fatalf("environment variable POSTGRES_USR is not set")
|
||||
}
|
||||
dbPortStr := os.Getenv("POSTGRESQL_PORT")
|
||||
if len(dbPortStr) == 0 {
|
||||
log.Fatalf("environment variable POSTGRES_PORT is not set")
|
||||
}
|
||||
dbPort, err := strconv.Atoi(dbPortStr)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid POSTGRESQL_PORT: %v", err)
|
||||
}
|
||||
|
||||
dbPassword := os.Getenv("POSTGRESQL_PWD")
|
||||
dbDatabase := os.Getenv("POSTGRESQL_DATABASE")
|
||||
if len(dbDatabase) == 0 {
|
||||
log.Fatalf("environment variable POSTGRESQL_DATABASE is not set")
|
||||
}
|
||||
|
||||
database := &models.Database{
|
||||
Type: "postgresql",
|
||||
PostGreSQL: &models.PostGreSQL{
|
||||
Host: dbHost,
|
||||
Port: dbPort,
|
||||
Username: dbUser,
|
||||
Password: dbPassword,
|
||||
Database: dbDatabase,
|
||||
},
|
||||
}
|
||||
|
||||
log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
|
||||
|
||||
if err := dao.InitDatabase(database); err != nil {
|
||||
log.Fatalf("failed to initialize database: %v", err)
|
||||
}
|
||||
test.InitDatabaseFromEnv()
|
||||
|
||||
// add project
|
||||
id, err := dao.AddProject(*private)
|
||||
@ -136,7 +99,7 @@ func TestIsSolutionUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHasPullPerm(t *testing.T) {
|
||||
policies := []*rbac.Policy{
|
||||
policies := []*types.Policy{
|
||||
{
|
||||
Resource: rbac.Resource(fmt.Sprintf("/project/%d/repository", private.ProjectID)),
|
||||
Action: rbac.ActionPull,
|
||||
@ -153,7 +116,7 @@ func TestHasPullPerm(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHasPushPerm(t *testing.T) {
|
||||
policies := []*rbac.Policy{
|
||||
policies := []*types.Policy{
|
||||
{
|
||||
Resource: rbac.Resource(fmt.Sprintf("/project/%d/repository", private.ProjectID)),
|
||||
Action: rbac.ActionPush,
|
||||
@ -170,7 +133,7 @@ func TestHasPushPerm(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHasPushPullPerm(t *testing.T) {
|
||||
policies := []*rbac.Policy{
|
||||
policies := []*types.Policy{
|
||||
{
|
||||
Resource: rbac.Resource(fmt.Sprintf("/project/%d/repository", private.ProjectID)),
|
||||
Action: rbac.ActionPush,
|
||||
|
@ -1,15 +1,14 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/rbac/project"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// robot implement the rbac.User interface for project robot account
|
||||
type robot struct {
|
||||
username string
|
||||
namespace rbac.Namespace
|
||||
policies []*rbac.Policy
|
||||
namespace types.Namespace
|
||||
policies []*types.Policy
|
||||
}
|
||||
|
||||
// GetUserName get the robot name.
|
||||
@ -18,22 +17,17 @@ func (r *robot) GetUserName() string {
|
||||
}
|
||||
|
||||
// GetPolicies ...
|
||||
func (r *robot) GetPolicies() []*rbac.Policy {
|
||||
policies := []*rbac.Policy{}
|
||||
if r.namespace.IsPublic() {
|
||||
policies = append(policies, project.PoliciesForPublicProject(r.namespace)...)
|
||||
}
|
||||
policies = append(policies, r.policies...)
|
||||
return policies
|
||||
func (r *robot) GetPolicies() []*types.Policy {
|
||||
return r.policies
|
||||
}
|
||||
|
||||
// GetRoles robot has no definition of role, always return nil here.
|
||||
func (r *robot) GetRoles() []rbac.Role {
|
||||
func (r *robot) GetRoles() []types.RBACRole {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRobot ...
|
||||
func NewRobot(username string, namespace rbac.Namespace, policies []*rbac.Policy) rbac.User {
|
||||
func NewRobot(username string, namespace types.Namespace, policies []*types.Policy) types.RBACUser {
|
||||
return &robot{
|
||||
username: username,
|
||||
namespace: namespace,
|
||||
@ -41,28 +35,13 @@ func NewRobot(username string, namespace rbac.Namespace, policies []*rbac.Policy
|
||||
}
|
||||
}
|
||||
|
||||
func filterPolicies(namespace rbac.Namespace, policies []*rbac.Policy) []*rbac.Policy {
|
||||
var results []*rbac.Policy
|
||||
if len(policies) == 0 {
|
||||
return results
|
||||
}
|
||||
|
||||
mp := getAllPolicies(namespace)
|
||||
func filterPolicies(namespace types.Namespace, policies []*types.Policy) []*types.Policy {
|
||||
var results []*types.Policy
|
||||
for _, policy := range policies {
|
||||
if mp[policy.String()] {
|
||||
if types.ResourceAllowedInNamespace(policy.Resource, namespace) {
|
||||
results = append(results, policy)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// getAllPolicies gets all of supported policies supported in project and external policies supported for robot account
|
||||
func getAllPolicies(namespace rbac.Namespace) map[string]bool {
|
||||
mp := map[string]bool{}
|
||||
for _, policy := range project.GetAllPolicies(namespace) {
|
||||
mp[policy.String()] = true
|
||||
}
|
||||
scannerPull := &rbac.Policy{Resource: namespace.Resource(rbac.ResourceRepository), Action: rbac.ActionScannerPull}
|
||||
mp[scannerPull.String()] = true
|
||||
return mp
|
||||
}
|
||||
|
@ -18,21 +18,22 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetPolicies(t *testing.T) {
|
||||
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: "/project/libray/repository",
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
robot := robot{
|
||||
username: "test",
|
||||
namespace: rbac.NewProjectNamespace(1, false),
|
||||
namespace: rbac.NewProjectNamespace(1),
|
||||
policies: policies,
|
||||
}
|
||||
|
||||
@ -42,13 +43,13 @@ func TestGetPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewRobot(t *testing.T) {
|
||||
policies := []*rbac.Policy{
|
||||
policies := []*types.Policy{
|
||||
{Resource: "/project/1/repository", Action: "pull"},
|
||||
{Resource: "/project/1/repository", Action: "scanner-pull"},
|
||||
{Resource: "/project/library/repository", Action: "pull"},
|
||||
{Resource: "/project/library/repository", Action: "push"},
|
||||
}
|
||||
|
||||
robot := NewRobot("test", rbac.NewProjectNamespace(1, false), policies)
|
||||
robot := NewRobot("test", rbac.NewProjectNamespace(1), policies)
|
||||
assert.Len(t, robot.GetPolicies(), 2)
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ import (
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/secret"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// SecurityContext implements security.Context interface based on secret store
|
||||
@ -79,7 +79,7 @@ func (s *SecurityContext) IsSolutionUser() bool {
|
||||
// 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 types.Action, resource types.Resource) bool {
|
||||
if s.store == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/chartserver"
|
||||
"github.com/goharbor/harbor/src/common/api"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/core/promgr/metamgr"
|
||||
)
|
||||
|
||||
@ -265,58 +264,3 @@ func (mpm *mockProjectManager) GetPublic() ([]*models.Project, error) {
|
||||
func (mpm *mockProjectManager) GetMetadataManager() metamgr.ProjectMetadataManager {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mock security context
|
||||
type mockSecurityContext struct{}
|
||||
|
||||
// IsAuthenticated returns whether the context has been authenticated or not
|
||||
func (msc *mockSecurityContext) IsAuthenticated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsername returns the username of user related to the context
|
||||
func (msc *mockSecurityContext) GetUsername() string {
|
||||
return "amdin"
|
||||
}
|
||||
|
||||
// IsSysAdmin returns whether the user is system admin
|
||||
func (msc *mockSecurityContext) IsSysAdmin() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSolutionUser returns whether the user is solution user
|
||||
func (msc *mockSecurityContext) IsSolutionUser() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Can returns whether the user can do action on resource
|
||||
func (msc *mockSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
|
||||
namespace, err := resource.GetNamespace()
|
||||
if err != nil || namespace.Kind() != "project" {
|
||||
return false
|
||||
}
|
||||
|
||||
projectIDOrName := namespace.Identity()
|
||||
|
||||
if projectIDOrName == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ns, ok := projectIDOrName.(string); ok {
|
||||
if ns == "library" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Get current user's all project
|
||||
func (msc *mockSecurityContext) GetMyProjects() ([]*models.Project, error) {
|
||||
return []*models.Project{{ProjectID: 0, Name: "library"}}, nil
|
||||
}
|
||||
|
||||
// Get user's role in provided project
|
||||
func (msc *mockSecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
|
||||
return []int{0, 1, 2, 3}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/rbac/project"
|
||||
"github.com/goharbor/harbor/src/pkg/q"
|
||||
"github.com/goharbor/harbor/src/pkg/robot"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
@ -220,8 +219,7 @@ func validateRobotReq(p *models.Project, robotReq *model.RobotCreate) error {
|
||||
return errors.New("access required")
|
||||
}
|
||||
|
||||
namespace, _ := rbac.Resource(fmt.Sprintf("/project/%d", p.ProjectID)).GetNamespace()
|
||||
policies := project.GetAllPolicies(namespace)
|
||||
policies := rbac.GetPoliciesOfProject(p.ProjectID)
|
||||
|
||||
mp := map[string]bool{}
|
||||
for _, policy := range policies {
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
)
|
||||
|
||||
@ -32,13 +33,13 @@ var (
|
||||
)
|
||||
|
||||
func TestRobotAPIPost(t *testing.T) {
|
||||
res := rbac.Resource("/project/1")
|
||||
res := types.Resource("/project/1")
|
||||
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: res.Subresource(rbac.ResourceRepository),
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
tokenDuration := time.Duration(30) * time.Minute
|
||||
@ -114,7 +115,7 @@ func TestRobotAPIPost(t *testing.T) {
|
||||
Name: "test",
|
||||
Description: "resource not exist",
|
||||
ExpiresAt: expiresAt,
|
||||
Access: []*rbac.Policy{
|
||||
Access: []*types.Policy{
|
||||
{Resource: res.Subresource("foo"), Action: rbac.ActionCreate},
|
||||
},
|
||||
},
|
||||
@ -130,7 +131,7 @@ func TestRobotAPIPost(t *testing.T) {
|
||||
Name: "test",
|
||||
Description: "action not exist",
|
||||
ExpiresAt: expiresAt,
|
||||
Access: []*rbac.Policy{
|
||||
Access: []*types.Policy{
|
||||
{Resource: res.Subresource(rbac.ResourceRepository), Action: "foo"},
|
||||
},
|
||||
},
|
||||
@ -146,7 +147,7 @@ func TestRobotAPIPost(t *testing.T) {
|
||||
Name: "test",
|
||||
Description: "policy not exit",
|
||||
ExpiresAt: expiresAt,
|
||||
Access: []*rbac.Policy{
|
||||
Access: []*types.Policy{
|
||||
{Resource: res.Subresource(rbac.ResourceMember), Action: rbac.ActionPush},
|
||||
},
|
||||
},
|
||||
|
@ -17,7 +17,6 @@ package api
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/core/filter"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -26,11 +25,12 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/rbac/project"
|
||||
"github.com/goharbor/harbor/src/common/security/local"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/core/filter"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// UserAPI handles request to /api/users/{}
|
||||
@ -484,16 +484,12 @@ func (ua *UserAPI) ListUserPermissions() {
|
||||
relative := ua.Ctx.Input.Query("relative") == "true"
|
||||
|
||||
scope := rbac.Resource(ua.Ctx.Input.Query("scope"))
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
|
||||
namespace, err := scope.GetNamespace()
|
||||
if err == nil {
|
||||
switch namespace.Kind() {
|
||||
case "project":
|
||||
for _, policy := range project.GetAllPolicies(namespace) {
|
||||
if ua.SecurityCtx.Can(policy.Action, policy.Resource) {
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
if ns, ok := types.NamespaceFromResource(scope); ok {
|
||||
for _, policy := range ns.GetPolicies() {
|
||||
if ua.SecurityCtx.Can(policy.Action, policy.Resource) {
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
191
src/core/promgr/mocks/project_manager.go
Normal file
191
src/core/promgr/mocks/project_manager.go
Normal file
@ -0,0 +1,191 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
metamgr "github.com/goharbor/harbor/src/core/promgr/metamgr"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
models "github.com/goharbor/harbor/src/common/models"
|
||||
)
|
||||
|
||||
// ProjectManager is an autogenerated mock type for the ProjectManager type
|
||||
type ProjectManager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: _a0
|
||||
func (_m *ProjectManager) Create(_a0 *models.Project) (int64, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(*models.Project) int64); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*models.Project) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: projectIDOrName
|
||||
func (_m *ProjectManager) Delete(projectIDOrName interface{}) error {
|
||||
ret := _m.Called(projectIDOrName)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
|
||||
r0 = rf(projectIDOrName)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Exists provides a mock function with given fields: projectIDOrName
|
||||
func (_m *ProjectManager) Exists(projectIDOrName interface{}) (bool, error) {
|
||||
ret := _m.Called(projectIDOrName)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(interface{}) bool); ok {
|
||||
r0 = rf(projectIDOrName)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(interface{}) error); ok {
|
||||
r1 = rf(projectIDOrName)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: projectIDOrName
|
||||
func (_m *ProjectManager) Get(projectIDOrName interface{}) (*models.Project, error) {
|
||||
ret := _m.Called(projectIDOrName)
|
||||
|
||||
var r0 *models.Project
|
||||
if rf, ok := ret.Get(0).(func(interface{}) *models.Project); ok {
|
||||
r0 = rf(projectIDOrName)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.Project)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(interface{}) error); ok {
|
||||
r1 = rf(projectIDOrName)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetMetadataManager provides a mock function with given fields:
|
||||
func (_m *ProjectManager) GetMetadataManager() metamgr.ProjectMetadataManager {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 metamgr.ProjectMetadataManager
|
||||
if rf, ok := ret.Get(0).(func() metamgr.ProjectMetadataManager); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(metamgr.ProjectMetadataManager)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetPublic provides a mock function with given fields:
|
||||
func (_m *ProjectManager) GetPublic() ([]*models.Project, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 []*models.Project
|
||||
if rf, ok := ret.Get(0).(func() []*models.Project); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Project)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IsPublic provides a mock function with given fields: projectIDOrName
|
||||
func (_m *ProjectManager) IsPublic(projectIDOrName interface{}) (bool, error) {
|
||||
ret := _m.Called(projectIDOrName)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(interface{}) bool); ok {
|
||||
r0 = rf(projectIDOrName)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(interface{}) error); ok {
|
||||
r1 = rf(projectIDOrName)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// List provides a mock function with given fields: query
|
||||
func (_m *ProjectManager) List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error) {
|
||||
ret := _m.Called(query)
|
||||
|
||||
var r0 *models.ProjectQueryResult
|
||||
if rf, ok := ret.Get(0).(func(*models.ProjectQueryParam) *models.ProjectQueryResult); ok {
|
||||
r0 = rf(query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.ProjectQueryResult)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*models.ProjectQueryParam) error); ok {
|
||||
r1 = rf(query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: projectIDOrName, project
|
||||
func (_m *ProjectManager) Update(projectIDOrName interface{}, project *models.Project) error {
|
||||
ret := _m.Called(projectIDOrName, project)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(interface{}, *models.Project) error); ok {
|
||||
r0 = rf(projectIDOrName, project)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
39
src/pkg/permission/evaluator/admin/admin.go
Normal file
39
src/pkg/permission/evaluator/admin/admin.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
var _ evaluator.Evaluator = &Evaluator{}
|
||||
|
||||
// Evaluator the permission evaluator for the system administrator
|
||||
type Evaluator struct {
|
||||
username string
|
||||
}
|
||||
|
||||
// HasPermission always return true for the system administrator
|
||||
func (e *Evaluator) HasPermission(resource types.Resource, action types.Action) bool {
|
||||
log.Debugf("system administrator %s require %s action for resource %s", e.username, action, resource)
|
||||
return true
|
||||
}
|
||||
|
||||
// New returns evaluator.Evaluator for the system administrator
|
||||
func New(username string) *Evaluator {
|
||||
return &Evaluator{username: username}
|
||||
}
|
64
src/pkg/permission/evaluator/evaluator.go
Normal file
64
src/pkg/permission/evaluator/evaluator.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package evaluator
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// Evaluator the permission evaluator
|
||||
type Evaluator interface {
|
||||
// HasPermission returns true when user has action permission for the resource
|
||||
HasPermission(resource types.Resource, action types.Action) bool
|
||||
}
|
||||
|
||||
// Evaluators evaluator set
|
||||
type Evaluators []Evaluator
|
||||
|
||||
// Add adds an evaluator to a given slice of evaluators
|
||||
func (evaluators Evaluators) Add(newEvaluators ...Evaluator) Evaluators {
|
||||
for _, newEvaluator := range newEvaluators {
|
||||
if newEvaluator == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if items, ok := newEvaluator.(Evaluators); ok {
|
||||
evaluators = evaluators.Add(items...)
|
||||
} else {
|
||||
exists := false
|
||||
for _, evaluator := range evaluators {
|
||||
if evaluator == newEvaluator {
|
||||
exists = true
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
evaluators = append(evaluators, newEvaluator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return evaluators
|
||||
}
|
||||
|
||||
// HasPermission returns true when one of evaluator has action permission for the resource
|
||||
func (evaluators Evaluators) HasPermission(resource types.Resource, action types.Action) bool {
|
||||
for _, evaluator := range evaluators {
|
||||
if evaluator != nil && evaluator.HasPermission(resource, action) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -12,28 +12,29 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
package evaluator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type ProjectParserTestSuite struct {
|
||||
suite.Suite
|
||||
type mockEvaluator struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (suite *ProjectParserTestSuite) TestParse() {
|
||||
namespace, err := projectNamespaceParser(Resource("/project/1/image"))
|
||||
suite.Equal(namespace, &projectNamespace{projectID: 1})
|
||||
suite.Nil(err)
|
||||
|
||||
namespace, err = projectNamespaceParser(Resource("/fake/1/image"))
|
||||
suite.Nil(namespace)
|
||||
suite.Error(err)
|
||||
func (e *mockEvaluator) HasPermission(resource types.Resource, action types.Action) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestProjectParserTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ProjectParserTestSuite))
|
||||
func TestEvaluatorsAdd(t *testing.T) {
|
||||
eva1 := &mockEvaluator{name: "eva1"}
|
||||
eva2 := &mockEvaluator{name: "eva2"}
|
||||
eva3 := Evaluators{eva1, eva2}
|
||||
|
||||
var es1 Evaluators
|
||||
assert.Len(t, es1.Add(eva3), 2)
|
||||
assert.Len(t, es1.Add(eva1, eva2, eva3), 2)
|
||||
}
|
48
src/pkg/permission/evaluator/lazy/lazy.go
Normal file
48
src/pkg/permission/evaluator/lazy/lazy.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package lazy
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// EvaluatorFactory the permission evaluator factory
|
||||
type EvaluatorFactory func() evaluator.Evaluator
|
||||
|
||||
var _ evaluator.Evaluator = &Evaluator{}
|
||||
|
||||
// Evaluator lazy permission evaluator
|
||||
type Evaluator struct {
|
||||
factory EvaluatorFactory
|
||||
evaluator evaluator.Evaluator
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// HasPermission returns true when user has action permission for the resource
|
||||
func (l *Evaluator) HasPermission(resource types.Resource, action types.Action) bool {
|
||||
l.once.Do(func() {
|
||||
l.evaluator = l.factory()
|
||||
})
|
||||
|
||||
return l.evaluator != nil && l.evaluator.HasPermission(resource, action)
|
||||
}
|
||||
|
||||
// New returns lazy evaluator
|
||||
func New(factory EvaluatorFactory) *Evaluator {
|
||||
return &Evaluator{factory: factory}
|
||||
}
|
64
src/pkg/permission/evaluator/namespace/namespace.go
Normal file
64
src/pkg/permission/evaluator/namespace/namespace.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package namespace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// EvaluatorFactory returns the evaluator.Evaluator of the namespace
|
||||
type EvaluatorFactory func(types.Namespace) evaluator.Evaluator
|
||||
|
||||
var _ evaluator.Evaluator = &Evaluator{}
|
||||
|
||||
// Evaluator evaluator for the namespace
|
||||
type Evaluator struct {
|
||||
factory EvaluatorFactory
|
||||
namespaceKind string
|
||||
cache sync.Map
|
||||
}
|
||||
|
||||
// HasPermission returns true when user has action permission for the resource
|
||||
func (e *Evaluator) HasPermission(resource types.Resource, action types.Action) bool {
|
||||
ns, ok := types.NamespaceFromResource(resource)
|
||||
if ok && ns.Kind() == e.namespaceKind {
|
||||
var eva evaluator.Evaluator
|
||||
|
||||
key := fmt.Sprintf("%s:%v", ns.Kind(), ns.Identity())
|
||||
value, ok := e.cache.Load(key)
|
||||
if !ok {
|
||||
eva = e.factory(ns)
|
||||
e.cache.Store(key, eva)
|
||||
} else {
|
||||
eva, _ = value.(evaluator.Evaluator) // maybe value is nil
|
||||
}
|
||||
|
||||
return eva != nil && eva.HasPermission(resource, action)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// New returns permission evaluator for which support namespace
|
||||
func New(namespaceKind string, factory EvaluatorFactory) *Evaluator {
|
||||
return &Evaluator{
|
||||
namespaceKind: namespaceKind,
|
||||
factory: factory,
|
||||
}
|
||||
}
|
53
src/pkg/permission/evaluator/rbac/casbin.go
Normal file
53
src/pkg/permission/evaluator/rbac/casbin.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin"
|
||||
"github.com/casbin/casbin/model"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// Syntax for models see https://casbin.org/docs/en/syntax-for-models
|
||||
const modelText = `
|
||||
# Request definition
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
# Policy definition
|
||||
[policy_definition]
|
||||
p = sub, obj, act, eft
|
||||
|
||||
# Role definition
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
# Policy effect
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
||||
|
||||
# Matchers
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && (r.act == p.act || p.act == '*')
|
||||
`
|
||||
|
||||
func makeEnforcer(rbacUser types.RBACUser) *casbin.Enforcer {
|
||||
m := model.Model{}
|
||||
m.LoadModelFromText(modelText)
|
||||
|
||||
e := casbin.NewEnforcer(m, &adapter{rbacUser: rbacUser})
|
||||
e.AddFunction("keyMatch2", keyMatch2Func)
|
||||
return e
|
||||
}
|
104
src/pkg/permission/evaluator/rbac/casbin_adapter.go
Normal file
104
src/pkg/permission/evaluator/rbac/casbin_adapter.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casbin/model"
|
||||
"github.com/casbin/casbin/persist"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
|
||||
func policyLinesOfRole(rbacRole types.RBACRole) []string {
|
||||
lines := []string{}
|
||||
|
||||
roleName := rbacRole.GetRoleName()
|
||||
// returns empty policy lines if role name is empty
|
||||
if roleName == "" {
|
||||
return lines
|
||||
}
|
||||
|
||||
for _, policy := range rbacRole.GetPolicies() {
|
||||
line := fmt.Sprintf("p, %s, %s, %s, %s", roleName, policy.Resource, policy.Action, policy.GetEffect())
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func policyLinesOfRBACUser(rbacUser types.RBACUser) []string {
|
||||
lines := []string{}
|
||||
|
||||
username := rbacUser.GetUserName()
|
||||
for _, policy := range rbacUser.GetPolicies() {
|
||||
line := fmt.Sprintf("p, %s, %s, %s, %s", username, policy.Resource, policy.Action, policy.GetEffect())
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
type adapter struct {
|
||||
rbacUser types.RBACUser
|
||||
}
|
||||
|
||||
func (a *adapter) getPolicyLines() []string {
|
||||
lines := []string{}
|
||||
|
||||
username := a.rbacUser.GetUserName()
|
||||
// returns empty policy lines if username is empty
|
||||
if username == "" {
|
||||
return lines
|
||||
}
|
||||
|
||||
lines = append(lines, policyLinesOfRBACUser(a.rbacUser)...)
|
||||
|
||||
for _, role := range a.rbacUser.GetRoles() {
|
||||
lines = append(lines, policyLinesOfRole(role)...)
|
||||
lines = append(lines, fmt.Sprintf("g, %s, %s", username, role.GetRoleName()))
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (a *adapter) LoadPolicy(model model.Model) error {
|
||||
for _, line := range a.getPolicyLines() {
|
||||
persist.LoadPolicyLine(line, model)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adapter) SavePolicy(model model.Model) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (a *adapter) AddPolicy(sec string, ptype string, rule []string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (a *adapter) RemovePolicy(sec string, ptype string, rule []string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (a *adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
|
||||
return errNotImplemented
|
||||
}
|
102
src/pkg/permission/evaluator/rbac/casbin_match.go
Normal file
102
src/pkg/permission/evaluator/rbac/casbin_match.go
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
type regexpStore struct {
|
||||
entries sync.Map
|
||||
}
|
||||
|
||||
func (s *regexpStore) Get(key string, build func(string) *regexp.Regexp) *regexp.Regexp {
|
||||
value, ok := s.entries.Load(key)
|
||||
if !ok {
|
||||
value = build(key)
|
||||
s.entries.Store(key, value)
|
||||
}
|
||||
|
||||
return value.(*regexp.Regexp)
|
||||
}
|
||||
|
||||
func (s *regexpStore) Purge() {
|
||||
var keys []interface{}
|
||||
s.entries.Range(func(key, value interface{}) bool {
|
||||
keys = append(keys, key)
|
||||
return true
|
||||
})
|
||||
|
||||
for _, key := range keys {
|
||||
s.entries.Delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
store = ®expStore{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
startRegexpStorePurging(store, time.Hour*24)
|
||||
}
|
||||
|
||||
func startRegexpStorePurging(s *regexpStore, intervalDuration time.Duration) {
|
||||
go func() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
jitter := time.Duration(rand.Int()%60) * time.Minute
|
||||
log.Debugf("Starting regexp store purge in %s", jitter)
|
||||
time.Sleep(jitter)
|
||||
|
||||
for {
|
||||
s.Purge()
|
||||
log.Debugf("Starting regexp store purge in %s", intervalDuration)
|
||||
time.Sleep(intervalDuration)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func keyMatch2Build(key2 string) *regexp.Regexp {
|
||||
re := regexp.MustCompile(`(.*):[^/]+(.*)`)
|
||||
|
||||
key2 = strings.Replace(key2, "/*", "/.*", -1)
|
||||
for {
|
||||
if !strings.Contains(key2, "/:") {
|
||||
break
|
||||
}
|
||||
|
||||
key2 = re.ReplaceAllString(key2, "$1[^/]+$2")
|
||||
}
|
||||
|
||||
return regexp.MustCompile("^" + key2 + "$")
|
||||
}
|
||||
|
||||
// keyMatch2 determines whether key1 matches the pattern of key2, its behavior most likely the builtin KeyMatch2
|
||||
// except that the match of ("/project/1/robot", "/project/1") will return false
|
||||
func keyMatch2(key1 string, key2 string) bool {
|
||||
return store.Get(key2, keyMatch2Build).MatchString(key1)
|
||||
}
|
||||
|
||||
func keyMatch2Func(args ...interface{}) (interface{}, error) {
|
||||
name1 := args[0].(string)
|
||||
name2 := args[1].(string)
|
||||
|
||||
return keyMatch2(name1, name2), nil
|
||||
}
|
@ -16,6 +16,8 @@ package rbac
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_keyMatch2(t *testing.T) {
|
||||
@ -57,3 +59,26 @@ func Test_keyMatch2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexpStore(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
s := ®expStore{}
|
||||
|
||||
sLen := func() int {
|
||||
var l int
|
||||
s.entries.Range(func(key, value interface{}) bool {
|
||||
l++
|
||||
|
||||
return true
|
||||
})
|
||||
return l
|
||||
}
|
||||
|
||||
r1 := s.Get("key1", keyMatch2Build)
|
||||
r2 := s.Get("key1", keyMatch2Build)
|
||||
|
||||
assert.Equal(r1, r2)
|
||||
s.Purge()
|
||||
assert.Equal(0, sLen())
|
||||
}
|
48
src/pkg/permission/evaluator/rbac/rbac.go
Normal file
48
src/pkg/permission/evaluator/rbac/rbac.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/casbin/casbin"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
var _ evaluator.Evaluator = &Evaluator{}
|
||||
|
||||
// Evaluator the permission evaluator for rbac user
|
||||
type Evaluator struct {
|
||||
rbacUser types.RBACUser
|
||||
enforcer *casbin.Enforcer
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// HasPermission returns true when the rbac user has action permission for the resource
|
||||
func (e *Evaluator) HasPermission(resource types.Resource, action types.Action) bool {
|
||||
e.once.Do(func() {
|
||||
e.enforcer = makeEnforcer(e.rbacUser)
|
||||
})
|
||||
|
||||
return e.enforcer.Enforce(e.rbacUser.GetUserName(), resource.String(), action.String())
|
||||
}
|
||||
|
||||
// New returns evaluator.Evaluator for the RBACUser
|
||||
func New(rbacUser types.RBACUser) *Evaluator {
|
||||
return &Evaluator{
|
||||
rbacUser: rbacUser,
|
||||
}
|
||||
}
|
77
src/pkg/permission/types/namespace.go
Normal file
77
src/pkg/permission/types/namespace.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
parsesMu sync.RWMutex
|
||||
parses = map[string]NamespaceParse{}
|
||||
)
|
||||
|
||||
// NamespaceParse parse namespace from the resource
|
||||
type NamespaceParse func(Resource) (Namespace, bool)
|
||||
|
||||
// Namespace the namespace interface
|
||||
type Namespace interface {
|
||||
// Kind returns the kind of namespace
|
||||
Kind() string
|
||||
// Resource returns new resource for subresources with the namespace
|
||||
Resource(subresources ...Resource) Resource
|
||||
// Identity returns identity attached with namespace
|
||||
Identity() interface{}
|
||||
// GetPolicies returns all policies of the namespace
|
||||
GetPolicies() []*Policy
|
||||
}
|
||||
|
||||
// RegistryNamespaceParse ...
|
||||
func RegistryNamespaceParse(name string, parse NamespaceParse) {
|
||||
parsesMu.Lock()
|
||||
defer parsesMu.Unlock()
|
||||
if parse == nil {
|
||||
panic("permission: Register namespace parse is nil")
|
||||
}
|
||||
if _, dup := parses[name]; dup {
|
||||
panic("permission: Register called twice for namespace parse " + name)
|
||||
}
|
||||
|
||||
parses[name] = parse
|
||||
}
|
||||
|
||||
// NamespaceFromResource returns namespace from resource
|
||||
func NamespaceFromResource(resource Resource) (Namespace, bool) {
|
||||
parsesMu.RLock()
|
||||
defer parsesMu.RUnlock()
|
||||
|
||||
for _, parse := range parses {
|
||||
if ns, ok := parse(resource); ok {
|
||||
return ns, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ResourceAllowedInNamespace returns true when resource's namespace equal the ns
|
||||
func ResourceAllowedInNamespace(resource Resource, ns Namespace) bool {
|
||||
n, ok := NamespaceFromResource(resource)
|
||||
if ok {
|
||||
return n.Kind() == ns.Kind() && n.Identity() == ns.Identity()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
33
src/pkg/permission/types/rbac.go
Normal file
33
src/pkg/permission/types/rbac.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
// RBACRole the interface of rbac role
|
||||
type RBACRole interface {
|
||||
// GetRoleName returns the role identity, if empty string role's policies will be ignore
|
||||
GetRoleName() string
|
||||
// GetPolicies returns the policies of the role
|
||||
GetPolicies() []*Policy
|
||||
}
|
||||
|
||||
// RBACUser the interface of rbac user
|
||||
type RBACUser interface {
|
||||
// GetUserName returns the user identity, if empty string user's all policies will be ignore
|
||||
GetUserName() string
|
||||
// GetPolicies returns special policies of the user
|
||||
GetPolicies() []*Policy
|
||||
// GetRoles returns roles the user owned
|
||||
GetRoles() []RBACRole
|
||||
}
|
62
src/pkg/permission/types/resource.go
Normal file
62
src/pkg/permission/types/resource.go
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Resource the type of resource
|
||||
type Resource string
|
||||
|
||||
func (res Resource) String() string {
|
||||
return string(res)
|
||||
}
|
||||
|
||||
// RelativeTo returns relative resource to other resource
|
||||
func (res Resource) RelativeTo(other Resource) (Resource, error) {
|
||||
prefix := other.String()
|
||||
str := res.String()
|
||||
|
||||
if !strings.HasPrefix(str, prefix) {
|
||||
return Resource(""), errors.New("value error")
|
||||
}
|
||||
|
||||
relative := strings.TrimPrefix(strings.TrimPrefix(str, prefix), "/")
|
||||
if relative == "" {
|
||||
relative = "."
|
||||
}
|
||||
|
||||
return Resource(relative), nil
|
||||
}
|
||||
|
||||
// Subresource returns subresource
|
||||
func (res Resource) Subresource(resources ...Resource) Resource {
|
||||
elements := []string{res.String()}
|
||||
|
||||
for _, resource := range resources {
|
||||
elements = append(elements, resource.String())
|
||||
}
|
||||
|
||||
return Resource(path.Join(elements...))
|
||||
}
|
||||
|
||||
// GetNamespace returns namespace from resource
|
||||
func (res Resource) GetNamespace() (Namespace, error) {
|
||||
return nil, fmt.Errorf("no namespace found for %s", res)
|
||||
}
|
57
src/pkg/permission/types/types.go
Normal file
57
src/pkg/permission/types/types.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
const (
|
||||
// EffectAllow allow effect
|
||||
EffectAllow = Effect("allow")
|
||||
// EffectDeny deny effect
|
||||
EffectDeny = Effect("deny")
|
||||
)
|
||||
|
||||
// Action the type of action
|
||||
type Action string
|
||||
|
||||
func (act Action) String() string {
|
||||
return string(act)
|
||||
}
|
||||
|
||||
// Effect the type of effect
|
||||
type Effect string
|
||||
|
||||
func (eff Effect) String() string {
|
||||
return string(eff)
|
||||
}
|
||||
|
||||
// Policy the type of policy
|
||||
type Policy struct {
|
||||
Resource
|
||||
Action
|
||||
Effect
|
||||
}
|
||||
|
||||
// GetEffect returns effect of resource, default is allow
|
||||
func (p *Policy) GetEffect() string {
|
||||
eft := p.Effect
|
||||
if eft == "" {
|
||||
eft = EffectAllow
|
||||
}
|
||||
|
||||
return eft.String()
|
||||
}
|
||||
|
||||
func (p *Policy) String() string {
|
||||
return p.Resource.String() + ":" + p.Action.String() + ":" + p.GetEffect()
|
||||
}
|
@ -1,17 +1,19 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/utils/test"
|
||||
core_cfg "github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/q"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ControllerTestSuite struct {
|
||||
@ -41,11 +43,11 @@ func (s *ControllerTestSuite) TestRobotAccount() {
|
||||
|
||||
res := rbac.Resource("/project/1")
|
||||
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: res.Subresource(rbac.ResourceRepository),
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
tokenDuration := time.Duration(30) * time.Minute
|
||||
|
@ -1,11 +1,12 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"time"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// RobotTable is the name of table in DB that holds the robot object
|
||||
@ -45,13 +46,13 @@ type RobotQuery struct {
|
||||
|
||||
// RobotCreate ...
|
||||
type RobotCreate struct {
|
||||
Name string `json:"name"`
|
||||
ProjectID int64 `json:"pid"`
|
||||
Description string `json:"description"`
|
||||
Disabled bool `json:"disabled"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
Visible bool `json:"-"`
|
||||
Access []*rbac.Policy `json:"access"`
|
||||
Name string `json:"name"`
|
||||
ProjectID int64 `json:"pid"`
|
||||
Description string `json:"description"`
|
||||
Disabled bool `json:"disabled"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
Visible bool `json:"-"`
|
||||
Access []*types.Policy `json:"access"`
|
||||
}
|
||||
|
||||
// Pagination ...
|
||||
|
@ -2,16 +2,17 @@ package robot
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// Claim implements the interface of jwt.Claims
|
||||
type Claim struct {
|
||||
jwt.StandardClaims
|
||||
TokenID int64 `json:"id"`
|
||||
ProjectID int64 `json:"pid"`
|
||||
Access []*rbac.Policy `json:"access"`
|
||||
TokenID int64 `json:"id"`
|
||||
ProjectID int64 `json:"pid"`
|
||||
Access []*types.Policy `json:"access"`
|
||||
}
|
||||
|
||||
// Valid valid the claims "tokenID, projectID and access".
|
||||
|
@ -1,18 +1,19 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: "/project/libray/repository",
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
rClaims := &Claim{
|
||||
@ -25,11 +26,11 @@ func TestValid(t *testing.T) {
|
||||
|
||||
func TestUnValidTokenID(t *testing.T) {
|
||||
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: "/project/libray/repository",
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
rClaims := &Claim{
|
||||
@ -42,11 +43,11 @@ func TestUnValidTokenID(t *testing.T) {
|
||||
|
||||
func TestUnValidProjectID(t *testing.T) {
|
||||
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: "/project/libray/repository",
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
rClaims := &Claim{
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
robot_claim "github.com/goharbor/harbor/src/pkg/token/claims/robot"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -22,11 +22,11 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: "/project/libray/repository",
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
tokenID := int64(123)
|
||||
@ -50,11 +50,11 @@ func TestNew(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRaw(t *testing.T) {
|
||||
rbacPolicy := &rbac.Policy{
|
||||
rbacPolicy := &types.Policy{
|
||||
Resource: "/project/library/repository",
|
||||
Action: "pull",
|
||||
}
|
||||
policies := []*rbac.Policy{}
|
||||
policies := []*types.Policy{}
|
||||
policies = append(policies, rbacPolicy)
|
||||
|
||||
tokenID := int64(123)
|
||||
|
@ -27,7 +27,10 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/security"
|
||||
"github.com/goharbor/harbor/src/core/promgr/metamgr"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
securitytesting "github.com/goharbor/harbor/src/testing/common/security"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -77,56 +80,6 @@ func (mockPM) GetMetadataManager() metamgr.ProjectMetadataManager {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
type mockSC struct{}
|
||||
|
||||
func (mockSC) Name() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
func (mockSC) IsAuthenticated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (mockSC) GetUsername() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
func (mockSC) IsSysAdmin() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mockSC) IsSolutionUser() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mockSC) GetMyProjects() ([]*models.Project, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (mockSC) GetProjectRoles(projectIDOrName interface{}) []int {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (mockSC) Can(action rbac.Action, resource rbac.Resource) bool {
|
||||
ns, _ := resource.GetNamespace()
|
||||
perms := map[int64]map[rbac.Action]struct{}{
|
||||
1: {
|
||||
rbac.ActionPull: {},
|
||||
rbac.ActionPush: {},
|
||||
},
|
||||
2: {
|
||||
rbac.ActionPull: {},
|
||||
},
|
||||
}
|
||||
pid := ns.Identity().(int64)
|
||||
m, ok := perms[pid]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
_, ok = m[action]
|
||||
return ok
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
checker = reqChecker{
|
||||
pm: mockPM{},
|
||||
@ -141,7 +94,28 @@ func TestMiddleware(t *testing.T) {
|
||||
w.WriteHeader(200)
|
||||
})
|
||||
|
||||
baseCtx := security.NewContext(context.Background(), mockSC{})
|
||||
sc := &securitytesting.Context{}
|
||||
sc.On("IsAuthenticated").Return(true)
|
||||
sc.On("IsSysAdmin").Return(false)
|
||||
mock.OnAnything(sc, "Can").Return(func(action types.Action, resource types.Resource) bool {
|
||||
perms := map[string]map[rbac.Action]struct{}{
|
||||
"/project/1/repository": {
|
||||
rbac.ActionPull: {},
|
||||
rbac.ActionPush: {},
|
||||
},
|
||||
"/project/2/repository": {
|
||||
rbac.ActionPull: {},
|
||||
},
|
||||
}
|
||||
m, ok := perms[resource.String()]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
_, ok = m[action]
|
||||
return ok
|
||||
})
|
||||
|
||||
baseCtx := security.NewContext(context.Background(), sc)
|
||||
ar1 := &middleware.ArtifactInfo{
|
||||
Repository: "project_1/hello-world",
|
||||
Reference: "v1",
|
||||
|
17
src/testing/common/common.go
Normal file
17
src/testing/common/common.go
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package common
|
||||
|
||||
//go:generate mockery -case snake -dir ../../common/security -name Context -output ./security -outpkg security
|
138
src/testing/common/security/context.go
Normal file
138
src/testing/common/security/context.go
Normal file
@ -0,0 +1,138 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
models "github.com/goharbor/harbor/src/common/models"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
)
|
||||
|
||||
// Context is an autogenerated mock type for the Context type
|
||||
type Context struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Can provides a mock function with given fields: action, resource
|
||||
func (_m *Context) Can(action types.Action, resource types.Resource) bool {
|
||||
ret := _m.Called(action, resource)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Action, types.Resource) bool); ok {
|
||||
r0 = rf(action, resource)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetMyProjects provides a mock function with given fields:
|
||||
func (_m *Context) GetMyProjects() ([]*models.Project, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 []*models.Project
|
||||
if rf, ok := ret.Get(0).(func() []*models.Project); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Project)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetProjectRoles provides a mock function with given fields: projectIDOrName
|
||||
func (_m *Context) GetProjectRoles(projectIDOrName interface{}) []int {
|
||||
ret := _m.Called(projectIDOrName)
|
||||
|
||||
var r0 []int
|
||||
if rf, ok := ret.Get(0).(func(interface{}) []int); ok {
|
||||
r0 = rf(projectIDOrName)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]int)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetUsername provides a mock function with given fields:
|
||||
func (_m *Context) GetUsername() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// IsAuthenticated provides a mock function with given fields:
|
||||
func (_m *Context) IsAuthenticated() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// IsSolutionUser provides a mock function with given fields:
|
||||
func (_m *Context) IsSolutionUser() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// IsSysAdmin provides a mock function with given fields:
|
||||
func (_m *Context) IsSysAdmin() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *Context) Name() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
Loading…
Reference in New Issue
Block a user