mirror of https://github.com/goharbor/harbor.git
149 lines
4.5 KiB
Go
149 lines
4.5 KiB
Go
// 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 robot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/goharbor/harbor/src/common/rbac"
|
|
rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
|
|
"github.com/goharbor/harbor/src/common/rbac/system"
|
|
"github.com/goharbor/harbor/src/controller/project"
|
|
"github.com/goharbor/harbor/src/controller/robot"
|
|
"github.com/goharbor/harbor/src/pkg/permission/evaluator"
|
|
"github.com/goharbor/harbor/src/pkg/permission/types"
|
|
"github.com/goharbor/harbor/src/pkg/project/models"
|
|
)
|
|
|
|
// SecurityContext implements security.Context interface based on database
|
|
type SecurityContext struct {
|
|
robot *robot.Robot
|
|
ctl project.Controller
|
|
evaluator evaluator.Evaluator
|
|
once sync.Once
|
|
}
|
|
|
|
// NewSecurityContext ...
|
|
func NewSecurityContext(r *robot.Robot) *SecurityContext {
|
|
return &SecurityContext{
|
|
ctl: project.Ctl,
|
|
robot: r,
|
|
}
|
|
}
|
|
|
|
// Name returns the name of the security context
|
|
func (s *SecurityContext) Name() string {
|
|
return "robot"
|
|
}
|
|
|
|
// IsAuthenticated returns true if the user has been authenticated
|
|
func (s *SecurityContext) IsAuthenticated() bool {
|
|
return s.robot != nil
|
|
}
|
|
|
|
// GetUsername returns the username of the authenticated user
|
|
// It returns null if the user has not been authenticated
|
|
func (s *SecurityContext) GetUsername() string {
|
|
if !s.IsAuthenticated() {
|
|
return ""
|
|
}
|
|
return s.robot.Name
|
|
}
|
|
|
|
// User get the current user
|
|
func (s *SecurityContext) User() *robot.Robot {
|
|
return s.robot
|
|
}
|
|
|
|
// IsSysAdmin robot cannot be a system admin
|
|
func (s *SecurityContext) IsSysAdmin() bool {
|
|
return false
|
|
}
|
|
|
|
// IsSolutionUser robot cannot be a system admin
|
|
func (s *SecurityContext) IsSolutionUser() bool {
|
|
return false
|
|
}
|
|
|
|
// Can returns whether the robot can do action on resource
|
|
func (s *SecurityContext) Can(ctx context.Context, action types.Action, resource types.Resource) bool {
|
|
if s.robot == nil {
|
|
return false
|
|
}
|
|
|
|
s.once.Do(func() {
|
|
var accesses []*types.Policy
|
|
for _, p := range s.robot.Permissions {
|
|
for _, a := range p.Access {
|
|
accesses = append(accesses, &types.Policy{
|
|
Action: a.Action,
|
|
Effect: a.Effect,
|
|
Resource: types.Resource(getPolicyResource(p, a)),
|
|
})
|
|
}
|
|
}
|
|
|
|
if s.robot.Level == robot.LEVELSYSTEM {
|
|
var proPolicies []*types.Policy
|
|
var sysPolicies []*types.Policy
|
|
var evaluators evaluator.Evaluators
|
|
for _, p := range accesses {
|
|
if strings.HasPrefix(p.Resource.String(), robot.SCOPESYSTEM) {
|
|
sysPolicies = append(sysPolicies, p)
|
|
} else if strings.HasPrefix(p.Resource.String(), robot.SCOPEPROJECT) {
|
|
proPolicies = append(proPolicies, p)
|
|
}
|
|
}
|
|
if len(sysPolicies) != 0 {
|
|
evaluators = evaluators.Add(system.NewEvaluator(s.GetUsername(), sysPolicies))
|
|
}
|
|
if len(proPolicies) != 0 {
|
|
evaluators = evaluators.Add(rbac_project.NewEvaluator(s.ctl, rbac_project.NewBuilderForPolicies(s.GetUsername(), proPolicies)))
|
|
}
|
|
s.evaluator = evaluators
|
|
} else {
|
|
s.evaluator = rbac_project.NewEvaluator(s.ctl, rbac_project.NewBuilderForPolicies(s.GetUsername(), accesses, filterRobotPolicies))
|
|
}
|
|
})
|
|
return s.evaluator != nil && s.evaluator.HasPermission(ctx, resource, action)
|
|
}
|
|
|
|
func filterRobotPolicies(p *models.Project, policies []*types.Policy) []*types.Policy {
|
|
namespace := rbac_project.NewNamespace(p.ProjectID)
|
|
|
|
var results []*types.Policy
|
|
for _, policy := range policies {
|
|
if types.ResourceAllowedInNamespace(policy.Resource, namespace) {
|
|
results = append(results, policy)
|
|
// give the PUSH action a pull access
|
|
if policy.Action == rbac.ActionPush {
|
|
results = append(results, &types.Policy{Resource: policy.Resource, Action: rbac.ActionPull})
|
|
}
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
// getPolicyResource to determine permissions for the project resource, the path should be /project instead of /project/project.
|
|
func getPolicyResource(perm *robot.Permission, pol *types.Policy) string {
|
|
if strings.HasPrefix(perm.Scope, robot.SCOPEPROJECT) && pol.Resource == rbac.ResourceProject {
|
|
return perm.Scope
|
|
}
|
|
return fmt.Sprintf("%s/%s", perm.Scope, pol.Resource)
|
|
}
|