mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 02:35:17 +01:00
Implement current security interfaces using ram
Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
parent
b5788f0695
commit
ebd26c0105
57
src/common/ram/namespace.go
Normal file
57
src/common/ram/namespace.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 ram
|
||||
|
||||
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 {
|
||||
projectIDOrName interface{}
|
||||
isPublic bool
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Kind() string {
|
||||
return "project"
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Resource(subresources ...Resource) Resource {
|
||||
return Resource(fmt.Sprintf("/project/%v", ns.projectIDOrName)).Subresource(subresources...)
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) Identity() interface{} {
|
||||
return ns.projectIDOrName
|
||||
}
|
||||
|
||||
func (ns *projectNamespace) IsPublic() bool {
|
||||
return ns.isPublic
|
||||
}
|
||||
|
||||
// NewProjectNamespace returns namespace for project
|
||||
func NewProjectNamespace(projectIDOrName interface{}, isPublic bool) Namespace {
|
||||
return &projectNamespace{projectIDOrName: projectIDOrName, isPublic: isPublic}
|
||||
}
|
45
src/common/ram/namespace_test.go
Normal file
45
src/common/ram/namespace_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 ram
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ProjectNamespaceTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *ProjectNamespaceTestSuite) TestResource() {
|
||||
var namespace Namespace
|
||||
|
||||
namespace = &projectNamespace{projectIDOrName: 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))
|
||||
|
||||
namespace, _ = Resource("/project/library/image").GetNamespace()
|
||||
suite.Equal(namespace.Identity(), "library")
|
||||
}
|
||||
|
||||
func TestProjectNamespaceTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ProjectNamespaceTestSuite))
|
||||
}
|
50
src/common/ram/parser.go
Normal file
50
src/common/ram/parser.go
Normal file
@ -0,0 +1,50 @@
|
||||
// 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 ram
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
var projectIDOrName interface{}
|
||||
|
||||
id, err := strconv.ParseInt(matches[1], 10, 64)
|
||||
if err == nil {
|
||||
projectIDOrName = id
|
||||
} else {
|
||||
projectIDOrName = matches[1]
|
||||
}
|
||||
|
||||
return &projectNamespace{projectIDOrName: projectIDOrName}, nil
|
||||
}
|
39
src/common/ram/parser_test.go
Normal file
39
src/common/ram/parser_test.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 ram
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ProjectParserTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *ProjectParserTestSuite) TestParse() {
|
||||
namespace, err := projectNamespaceParser(Resource("/project/1/image"))
|
||||
suite.Equal(namespace, &projectNamespace{projectIDOrName: int64(1)})
|
||||
suite.Nil(err)
|
||||
|
||||
namespace, err = projectNamespaceParser(Resource("/fake/1/image"))
|
||||
suite.Nil(namespace)
|
||||
suite.Error(err)
|
||||
}
|
||||
|
||||
func TestProjectParserTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ProjectParserTestSuite))
|
||||
}
|
33
src/common/ram/project/const.go
Normal file
33
src/common/ram/project/const.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 project
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/ram"
|
||||
)
|
||||
|
||||
// const action variables
|
||||
const (
|
||||
ActionAll = ram.Action("*")
|
||||
ActionPull = ram.Action("pull")
|
||||
ActionPush = ram.Action("push")
|
||||
ActionPushPull = ram.Action("push+pull")
|
||||
)
|
||||
|
||||
// const resource variables
|
||||
const (
|
||||
ResourceAll = ram.Resource("*")
|
||||
ResourceImage = ram.Resource("image")
|
||||
)
|
59
src/common/ram/project/util.go
Normal file
59
src/common/ram/project/util.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 project
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/ram"
|
||||
)
|
||||
|
||||
var (
|
||||
// subresource policies for public project
|
||||
publicProjectPolicies = []*ram.Policy{
|
||||
{Resource: ResourceImage, Action: ActionPull},
|
||||
}
|
||||
|
||||
// subresource policies for system admin visitor
|
||||
systemAdminProjectPolicies = []*ram.Policy{
|
||||
{Resource: ResourceAll, Action: ActionAll},
|
||||
}
|
||||
)
|
||||
|
||||
func policiesForPublicProject(namespace ram.Namespace) []*ram.Policy {
|
||||
policies := []*ram.Policy{}
|
||||
|
||||
for _, policy := range publicProjectPolicies {
|
||||
policies = append(policies, &ram.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
||||
|
||||
func policiesForSystemAdmin(namespace ram.Namespace) []*ram.Policy {
|
||||
policies := []*ram.Policy{}
|
||||
|
||||
for _, policy := range systemAdminProjectPolicies {
|
||||
policies = append(policies, &ram.Policy{
|
||||
Resource: namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
82
src/common/ram/project/visitor.go
Normal file
82
src/common/ram/project/visitor.go
Normal file
@ -0,0 +1,82 @@
|
||||
// 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/ram"
|
||||
)
|
||||
|
||||
// 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 ram.User interface for project visitor
|
||||
type visitor struct {
|
||||
ctx visitorContext
|
||||
namespace ram.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() []*ram.Policy {
|
||||
if v.ctx.IsSysAdmin() {
|
||||
return policiesForSystemAdmin(v.namespace)
|
||||
}
|
||||
|
||||
if v.namespace.IsPublic() {
|
||||
return policiesForPublicProject(v.namespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRoles returns roles of the visitor
|
||||
func (v *visitor) GetRoles() []ram.Role {
|
||||
if !v.ctx.IsAuthenticated() {
|
||||
return nil
|
||||
}
|
||||
|
||||
roles := []ram.Role{}
|
||||
|
||||
for _, roleID := range v.projectRoles {
|
||||
roles = append(roles, &visitorRole{roleID: roleID, namespace: v.namespace})
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
// NewUser returns ram.User interface for the project visitor
|
||||
func NewUser(ctx visitorContext, namespace ram.Namespace, projectRoles ...int) ram.User {
|
||||
return &visitor{
|
||||
ctx: ctx,
|
||||
namespace: namespace,
|
||||
projectRoles: projectRoles,
|
||||
}
|
||||
}
|
79
src/common/ram/project/visitor_role.go
Normal file
79
src/common/ram/project/visitor_role.go
Normal file
@ -0,0 +1,79 @@
|
||||
// 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/ram"
|
||||
)
|
||||
|
||||
var (
|
||||
rolePoliciesMap = map[string][]*ram.Policy{
|
||||
"projectAdmin": {
|
||||
{Resource: ResourceImage, Action: ActionPushPull}, // compatible with security all perm of project
|
||||
{Resource: ResourceImage, Action: ActionPush},
|
||||
{Resource: ResourceImage, Action: ActionPull},
|
||||
},
|
||||
|
||||
"developer": {
|
||||
{Resource: ResourceImage, Action: ActionPush},
|
||||
{Resource: ResourceImage, Action: ActionPull},
|
||||
},
|
||||
|
||||
"guest": {
|
||||
{Resource: ResourceImage, Action: ActionPull},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// visitorRole implement the ram.Role interface
|
||||
type visitorRole struct {
|
||||
namespace ram.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.RoleDeveloper:
|
||||
return "developer"
|
||||
case common.RoleGuest:
|
||||
return "guest"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// GetPolicies returns policies for the visitor role
|
||||
func (role *visitorRole) GetPolicies() []*ram.Policy {
|
||||
policies := []*ram.Policy{}
|
||||
|
||||
roleName := role.GetRoleName()
|
||||
if roleName == "" {
|
||||
return policies
|
||||
}
|
||||
|
||||
for _, policy := range rolePoliciesMap[roleName] {
|
||||
policies = append(policies, &ram.Policy{
|
||||
Resource: role.namespace.Resource(policy.Resource),
|
||||
Action: policy.Action,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
44
src/common/ram/project/visitor_role_test.go
Normal file
44
src/common/ram/project/visitor_role_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
// 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")
|
||||
|
||||
unknow := visitorRole{roleID: 404}
|
||||
suite.Equal(unknow.GetRoleName(), "")
|
||||
}
|
||||
|
||||
func TestVisitorRoleTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(VisitorRoleTestSuite))
|
||||
}
|
93
src/common/ram/project/visitor_test.go
Normal file
93
src/common/ram/project/visitor_test.go
Normal file
@ -0,0 +1,93 @@
|
||||
// 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/ram"
|
||||
"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 := ram.NewProjectNamespace("library", false)
|
||||
publicNamespace := ram.NewProjectNamespace("library", 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(), policiesForSystemAdmin(namespace))
|
||||
|
||||
systemAdminForPublicProject := NewUser(sysAdminCtx, publicNamespace)
|
||||
suite.Equal(systemAdminForPublicProject.GetPolicies(), policiesForSystemAdmin(publicNamespace))
|
||||
}
|
||||
|
||||
func (suite *VisitorTestSuite) TestGetRoles() {
|
||||
namespace := ram.NewProjectNamespace("library", 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))
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
package ram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
)
|
||||
|
||||
@ -43,6 +44,18 @@ func (res Resource) Subresource(resources ...Resource) Resource {
|
||||
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
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
package ram
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -355,3 +356,37 @@ func TestResource_Subresource(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,10 @@
|
||||
package admiral
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/ram"
|
||||
"github.com/goharbor/harbor/src/common/ram/project"
|
||||
"github.com/goharbor/harbor/src/common/security/admiral/authcontext"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
)
|
||||
|
||||
@ -71,70 +71,33 @@ func (s *SecurityContext) IsSolutionUser() bool {
|
||||
|
||||
// HasReadPerm returns whether the user has read permission to the project
|
||||
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
|
||||
public, err := s.pm.IsPublic(projectIDOrName)
|
||||
if err != nil {
|
||||
log.Errorf("failed to check the public of project %v: %v",
|
||||
projectIDOrName, err)
|
||||
return false
|
||||
}
|
||||
if public {
|
||||
return true
|
||||
}
|
||||
|
||||
// private project
|
||||
if !s.IsAuthenticated() {
|
||||
return false
|
||||
}
|
||||
|
||||
// system admin
|
||||
if s.IsSysAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
roles := s.GetProjectRoles(projectIDOrName)
|
||||
|
||||
return len(roles) > 0
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
return s.Can(project.ActionPull, ram.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(project.ResourceImage))
|
||||
}
|
||||
|
||||
// HasWritePerm returns whether the user has write permission to the project
|
||||
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
|
||||
if !s.IsAuthenticated() {
|
||||
return false
|
||||
}
|
||||
|
||||
// system admin
|
||||
if s.IsSysAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
roles := s.GetProjectRoles(projectIDOrName)
|
||||
for _, role := range roles {
|
||||
switch role {
|
||||
case common.RoleProjectAdmin,
|
||||
common.RoleDeveloper:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
return s.Can(project.ActionPush, ram.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(project.ResourceImage))
|
||||
}
|
||||
|
||||
// HasAllPerm returns whether the user has all permissions to the project
|
||||
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
|
||||
if !s.IsAuthenticated() {
|
||||
return false
|
||||
}
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
return s.Can(project.ActionPushPull, ram.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(project.ResourceImage))
|
||||
}
|
||||
|
||||
// system admin
|
||||
if s.IsSysAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
roles := s.GetProjectRoles(projectIDOrName)
|
||||
for _, role := range roles {
|
||||
switch role {
|
||||
case common.RoleProjectAdmin:
|
||||
return true
|
||||
// Can returns whether the user can do action on resource
|
||||
func (s *SecurityContext) Can(action ram.Action, resource ram.Resource) bool {
|
||||
ns, err := resource.GetNamespace()
|
||||
if err == nil {
|
||||
switch ns.Kind() {
|
||||
case "project":
|
||||
projectIDOrName := ns.Identity()
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
projectNamespace := ram.NewProjectNamespace(projectIDOrName, isPublicProject)
|
||||
user := project.NewUser(s, projectNamespace, s.GetProjectRoles(projectIDOrName)...)
|
||||
return ram.HasPermission(user, resource, action)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/dao/group"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/ram"
|
||||
"github.com/goharbor/harbor/src/common/ram/project"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
)
|
||||
@ -67,67 +69,36 @@ func (s *SecurityContext) IsSolutionUser() bool {
|
||||
|
||||
// HasReadPerm returns whether the user has read permission to the project
|
||||
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
|
||||
// public project
|
||||
public, err := s.pm.IsPublic(projectIDOrName)
|
||||
if err != nil {
|
||||
log.Errorf("failed to check the public of project %v: %v",
|
||||
projectIDOrName, err)
|
||||
return false
|
||||
}
|
||||
if public {
|
||||
return true
|
||||
}
|
||||
|
||||
// private project
|
||||
if !s.IsAuthenticated() {
|
||||
return false
|
||||
}
|
||||
|
||||
// system admin
|
||||
if s.IsSysAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
roles := s.GetProjectRoles(projectIDOrName)
|
||||
return len(roles) > 0
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
return s.Can(project.ActionPull, ram.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(project.ResourceImage))
|
||||
}
|
||||
|
||||
// HasWritePerm returns whether the user has write permission to the project
|
||||
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
|
||||
if !s.IsAuthenticated() {
|
||||
return false
|
||||
}
|
||||
// system admin
|
||||
if s.IsSysAdmin() {
|
||||
return true
|
||||
}
|
||||
roles := s.GetProjectRoles(projectIDOrName)
|
||||
for _, role := range roles {
|
||||
switch role {
|
||||
case common.RoleProjectAdmin,
|
||||
common.RoleDeveloper:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
return s.Can(project.ActionPush, ram.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(project.ResourceImage))
|
||||
}
|
||||
|
||||
// HasAllPerm returns whether the user has all permissions to the project
|
||||
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
|
||||
if !s.IsAuthenticated() {
|
||||
return false
|
||||
}
|
||||
// system admin
|
||||
if s.IsSysAdmin() {
|
||||
return true
|
||||
}
|
||||
roles := s.GetProjectRoles(projectIDOrName)
|
||||
for _, role := range roles {
|
||||
switch role {
|
||||
case common.RoleProjectAdmin:
|
||||
return true
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
return s.Can(project.ActionPushPull, ram.NewProjectNamespace(projectIDOrName, isPublicProject).Resource(project.ResourceImage))
|
||||
}
|
||||
|
||||
// Can returns whether the user can do action on resource
|
||||
func (s *SecurityContext) Can(action ram.Action, resource ram.Resource) bool {
|
||||
ns, err := resource.GetNamespace()
|
||||
if err == nil {
|
||||
switch ns.Kind() {
|
||||
case "project":
|
||||
projectIDOrName := ns.Identity()
|
||||
isPublicProject, _ := s.pm.IsPublic(projectIDOrName)
|
||||
projectNamespace := ram.NewProjectNamespace(projectIDOrName, isPublicProject)
|
||||
user := project.NewUser(s, projectNamespace, s.GetProjectRoles(projectIDOrName)...)
|
||||
return ram.HasPermission(user, resource, action)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/ram"
|
||||
"github.com/goharbor/harbor/src/common/secret"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
@ -97,6 +98,14 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
|
||||
return s.store.GetUsername(s.secret) == secret.JobserviceUser || s.store.GetUsername(s.secret) == secret.CoreUser
|
||||
}
|
||||
|
||||
// Can returns whether the user can do action on resource
|
||||
func (s *SecurityContext) Can(action ram.Action, resource ram.Resource) bool {
|
||||
if s.store == nil {
|
||||
return false
|
||||
}
|
||||
return s.store.GetUsername(s.secret) == secret.JobserviceUser || s.store.GetUsername(s.secret) == secret.CoreUser
|
||||
}
|
||||
|
||||
// GetMyProjects ...
|
||||
func (s *SecurityContext) GetMyProjects() ([]*models.Project, error) {
|
||||
return nil, fmt.Errorf("GetMyProjects is unsupported")
|
||||
|
Loading…
Reference in New Issue
Block a user