Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
4 contributors

Users who have contributed to this file

@hsluoyz @nodece @matevzmihalic @kenjones-cisco
299 lines (257 sloc) 6.79 KB
// Copyright 2017 The casbin Authors. 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 defaultrolemanager
import (
"errors"
"sync"
"github.com/casbin/casbin/log"
"github.com/casbin/casbin/rbac"
)
type MatchingFunc func(arg1, arg2 string) bool
// RoleManager provides a default implementation for the RoleManager interface
type RoleManager struct {
allRoles *sync.Map
maxHierarchyLevel int
hasPattern bool
matchingFunc MatchingFunc
}
// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
rm := RoleManager{}
rm.allRoles = &sync.Map{}
rm.maxHierarchyLevel = maxHierarchyLevel
rm.hasPattern = false
return &rm
}
func (rm *RoleManager) AddMatchingFunc(name string, fn MatchingFunc) {
rm.hasPattern = true
rm.matchingFunc = fn
}
func (rm *RoleManager) hasRole(name string) bool {
var ok bool
if rm.hasPattern {
rm.allRoles.Range(func(key, value interface{}) bool {
if rm.matchingFunc(name, key.(string)) {
ok = true
}
return true
})
} else {
_, ok = rm.allRoles.Load(name)
}
return ok
}
func (rm *RoleManager) createRole(name string) *Role {
if rm.hasPattern {
rm.allRoles.Range(func(key, value interface{}) bool {
if rm.matchingFunc(name, key.(string)) {
name = key.(string)
}
return true
})
}
role, _ := rm.allRoles.LoadOrStore(name, newRole(name))
return role.(*Role)
}
// Clear clears all stored data and resets the role manager to the initial state.
func (rm *RoleManager) Clear() error {
rm.allRoles = &sync.Map{}
return nil
}
// AddLink adds the inheritance link between role: name1 and role: name2.
// aka role: name1 inherits role: name2.
// domain is a prefix to the roles.
func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) error {
if len(domain) == 1 {
name1 = domain[0] + "::" + name1
name2 = domain[0] + "::" + name2
} else if len(domain) > 1 {
return errors.New("error: domain should be 1 parameter")
}
role1 := rm.createRole(name1)
role2 := rm.createRole(name2)
role1.addRole(role2)
return nil
}
// DeleteLink deletes the inheritance link between role: name1 and role: name2.
// aka role: name1 does not inherit role: name2 any more.
// domain is a prefix to the roles.
func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) error {
if len(domain) == 1 {
name1 = domain[0] + "::" + name1
name2 = domain[0] + "::" + name2
} else if len(domain) > 1 {
return errors.New("error: domain should be 1 parameter")
}
if !rm.hasRole(name1) || !rm.hasRole(name2) {
return errors.New("error: name1 or name2 does not exist")
}
role1 := rm.createRole(name1)
role2 := rm.createRole(name2)
role1.deleteRole(role2)
return nil
}
// HasLink determines whether role: name1 inherits role: name2.
// domain is a prefix to the roles.
func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) {
if len(domain) == 1 {
name1 = domain[0] + "::" + name1
name2 = domain[0] + "::" + name2
} else if len(domain) > 1 {
return false, errors.New("error: domain should be 1 parameter")
}
if name1 == name2 {
return true, nil
}
if !rm.hasRole(name1) || !rm.hasRole(name2) {
return false, nil
}
role1 := rm.createRole(name1)
return role1.hasRole(name2, rm.maxHierarchyLevel), nil
}
// GetRoles gets the roles that a subject inherits.
// domain is a prefix to the roles.
func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) {
if len(domain) == 1 {
name = domain[0] + "::" + name
} else if len(domain) > 1 {
return nil, errors.New("error: domain should be 1 parameter")
}
if !rm.hasRole(name) {
return []string{}, nil
}
roles := rm.createRole(name).getRoles()
if len(domain) == 1 {
for i := range roles {
roles[i] = roles[i][len(domain[0])+2:]
}
}
return roles, nil
}
// GetUsers gets the users that inherits a subject.
// domain is an unreferenced parameter here, may be used in other implementations.
func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) {
if len(domain) == 1 {
name = domain[0] + "::" + name
} else if len(domain) > 1 {
return nil, errors.New("error: domain should be 1 parameter")
}
if !rm.hasRole(name) {
return nil, errors.New("error: name does not exist")
}
names := []string{}
rm.allRoles.Range(func(_, value interface{}) bool {
role := value.(*Role)
if role.hasDirectRole(name) {
names = append(names, role.name)
}
return true
})
if len(domain) == 1 {
for i := range names {
names[i] = names[i][len(domain[0])+2:]
}
}
return names, nil
}
// PrintRoles prints all the roles to log.
func (rm *RoleManager) PrintRoles() error {
line := ""
rm.allRoles.Range(func(_, value interface{}) bool {
if text := value.(*Role).toString(); text != "" {
if line == "" {
line = text
} else {
line += ", " + text
}
}
return true
})
log.LogPrint(line)
return nil
}
// Role represents the data structure for a role in RBAC.
type Role struct {
name string
roles []*Role
}
func newRole(name string) *Role {
r := Role{}
r.name = name
return &r
}
func (r *Role) addRole(role *Role) {
for _, rr := range r.roles {
if rr.name == role.name {
return
}
}
r.roles = append(r.roles, role)
}
func (r *Role) deleteRole(role *Role) {
for i, rr := range r.roles {
if rr.name == role.name {
r.roles = append(r.roles[:i], r.roles[i+1:]...)
return
}
}
}
func (r *Role) hasRole(name string, hierarchyLevel int) bool {
if r.name == name {
return true
}
if hierarchyLevel <= 0 {
return false
}
for _, role := range r.roles {
if role.hasRole(name, hierarchyLevel-1) {
return true
}
}
return false
}
func (r *Role) hasDirectRole(name string) bool {
for _, role := range r.roles {
if role.name == name {
return true
}
}
return false
}
func (r *Role) toString() string {
names := ""
if len(r.roles) == 0 {
return ""
}
for i, role := range r.roles {
if i == 0 {
names += role.name
} else {
names += ", " + role.name
}
}
if len(r.roles) == 1 {
return r.name + " < " + names
} else {
return r.name + " < (" + names + ")"
}
}
func (r *Role) getRoles() []string {
names := []string{}
for _, role := range r.roles {
names = append(names, role.name)
}
return names
}
You can’t perform that action at this time.