Skip to content

Commit

Permalink
Merge pull request #2193 from ywk253100/170502_security_ctx_db
Browse files Browse the repository at this point in the history
Implement security context interface
  • Loading branch information
ywk253100 committed May 4, 2017
2 parents dd80ea4 + 22826b5 commit 13d9b05
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 16 deletions.
5 changes: 5 additions & 0 deletions src/common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const (
LDAPScopeOnelevel = "2"
LDAPScopeSubtree = "3"

RoleSystemAdmin = 1
RoleProjectAdmin = 2
RoleDeveloper = 3
RoleGuest = 4

ExtEndpoint = "ext_endpoint"
AUTHMode = "auth_mode"
DatabaseType = "database_type"
Expand Down
15 changes: 0 additions & 15 deletions src/common/security/db/context.go

This file was deleted.

117 changes: 117 additions & 0 deletions src/common/security/rbac/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,120 @@
// limitations under the License.

package rbac

import (
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/ui/pms"
)

// SecurityContext implements security.Context interface based on database
type SecurityContext struct {
user *models.User
pms pms.PMS
}

// NewSecurityContext ...
func NewSecurityContext(user *models.User, pms pms.PMS) *SecurityContext {
return &SecurityContext{
user: user,
pms: pms,
}
}

// IsAuthenticated returns true if the user has been authenticated
func (s *SecurityContext) IsAuthenticated() bool {
return s.user != 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.user.Username
}

// IsSysAdmin returns whether the authenticated user is system admin
// It returns false if the user has not been authenticated
func (s *SecurityContext) IsSysAdmin() bool {
if !s.IsAuthenticated() {
return false
}
return s.user.HasAdminRole == 1
}

// HasReadPerm returns whether the user has read permission to the project
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
// public project
if s.pms.IsPublic(projectIDOrName) {
return true
}

// private project
if !s.IsAuthenticated() {
return false
}

// system admin
if s.IsSysAdmin() {
return true
}

roles := s.pms.GetRoles(s.GetUsername(), projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin,
common.RoleDeveloper,
common.RoleGuest:
return true
}
}

return false
}

// 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.pms.GetRoles(s.GetUsername(), projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin,
common.RoleDeveloper:
return true
}
}

return false
}

// 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.pms.GetRoles(s.GetUsername(), projectIDOrName)
for _, role := range roles {
switch role {
case common.RoleProjectAdmin:
return true
}
}

return false
}
185 changes: 185 additions & 0 deletions src/common/security/rbac/context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// 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/assert"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
)

type fakePMS struct {
public string
roles map[string][]int
}

func (f *fakePMS) IsPublic(projectIDOrName interface{}) bool {
return f.public == projectIDOrName.(string)
}
func (f *fakePMS) GetRoles(username string, projectIDOrName interface{}) []int {
return f.roles[projectIDOrName.(string)]
}

func TestIsAuthenticated(t *testing.T) {
// unauthenticated
ctx := NewSecurityContext(nil, nil)
assert.False(t, ctx.IsAuthenticated())

// authenticated
ctx = NewSecurityContext(&models.User{
Username: "test",
}, nil)
assert.True(t, ctx.IsAuthenticated())
}

func TestGetUsername(t *testing.T) {
// unauthenticated
ctx := NewSecurityContext(nil, nil)
assert.Equal(t, "", ctx.GetUsername())

// authenticated
ctx = NewSecurityContext(&models.User{
Username: "test",
}, nil)
assert.Equal(t, "test", ctx.GetUsername())
}

func TestIsSysAdmin(t *testing.T) {
// unauthenticated
ctx := NewSecurityContext(nil, nil)
assert.False(t, ctx.IsSysAdmin())

// authenticated, non admin
ctx = NewSecurityContext(&models.User{
Username: "test",
}, nil)
assert.False(t, ctx.IsSysAdmin())

// authenticated, admin
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, nil)
assert.True(t, ctx.IsSysAdmin())
}

func TestHasReadPerm(t *testing.T) {
pms := &fakePMS{
public: "public_project",
roles: map[string][]int{
"has_read_perm_project": []int{common.RoleGuest},
},
}

// public project, unauthenticated
ctx := NewSecurityContext(nil, pms)
assert.True(t, ctx.HasReadPerm("public_project"))

// private project, unauthenticated
ctx = NewSecurityContext(nil, pms)
assert.False(t, ctx.HasReadPerm("has_read_perm_project"))

// private project, authenticated, has no perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.False(t, ctx.HasReadPerm("has_no_perm_project"))

// private project, authenticated, has read perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.True(t, ctx.HasReadPerm("has_read_perm_project"))

// private project, authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, pms)
assert.True(t, ctx.HasReadPerm("has_no_perm_project"))
}

func TestHasWritePerm(t *testing.T) {
pms := &fakePMS{
roles: map[string][]int{
"has_read_perm_project": []int{common.RoleGuest},
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
},
}

// unauthenticated
ctx := NewSecurityContext(nil, pms)
assert.False(t, ctx.HasWritePerm("has_write_perm_project"))

// authenticated, has read perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.False(t, ctx.HasWritePerm("has_read_perm_project")) // authenticated, has read perm

// authenticated, has write perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.True(t, ctx.HasWritePerm("has_write_perm_project"))

// authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, pms)
assert.True(t, ctx.HasReadPerm("has_no_perm_project"))
}

func TestHasAllPerm(t *testing.T) {
pms := &fakePMS{
roles: map[string][]int{
"has_read_perm_project": []int{common.RoleGuest},
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
"has_all_perm_project": []int{common.RoleGuest, common.RoleDeveloper, common.RoleProjectAdmin},
},
}

// unauthenticated
ctx := NewSecurityContext(nil, pms)
assert.False(t, ctx.HasAllPerm("has_all_perm_project"))

// authenticated, has read perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.False(t, ctx.HasAllPerm("has_read_perm_project"))

// authenticated, has write perm
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.False(t, ctx.HasAllPerm("has_write_perm_project"))

// authenticated, has all perms
ctx = NewSecurityContext(&models.User{
Username: "test",
}, pms)
assert.True(t, ctx.HasAllPerm("has_all_perm_project"))

// authenticated, system admin
ctx = NewSecurityContext(&models.User{
Username: "test",
HasAdminRole: 1,
}, pms)
assert.True(t, ctx.HasReadPerm("has_no_perm_project"))
}
4 changes: 3 additions & 1 deletion src/ui/pms/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package project
package pms

// PMS is the project mamagement service which abstracts
// the operations related to projects
type PMS interface {
IsPublic(projectIDOrName interface{}) bool
GetRoles(username string, projectIDOrName interface{}) []int
}

0 comments on commit 13d9b05

Please sign in to comment.