diff --git a/rbac/default-role-manager/role_manager.go b/rbac/default-role-manager/role_manager.go index d6e9d164..3fa98207 100644 --- a/rbac/default-role-manager/role_manager.go +++ b/rbac/default-role-manager/role_manager.go @@ -1,489 +1,67 @@ -// 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/v3/rbac" "github.com/casbin/casbin/v3/util" ) -const defaultDomain string = "" - -// Role represents the data structure for a role in RBAC. -type Role struct { - name string - roles *sync.Map - users *sync.Map - matched *sync.Map - matchedBy *sync.Map - linkConditionFuncMap *sync.Map - linkConditionFuncParamsMap *sync.Map -} - -func newRole(name string) *Role { - r := Role{} - r.name = name - r.roles = &sync.Map{} - r.users = &sync.Map{} - r.matched = &sync.Map{} - r.matchedBy = &sync.Map{} - r.linkConditionFuncMap = &sync.Map{} - r.linkConditionFuncParamsMap = &sync.Map{} - return &r -} - -func (r *Role) addRole(role *Role) { - r.roles.Store(role.name, role) - role.addUser(r) -} - -func (r *Role) removeRole(role *Role) { - r.roles.Delete(role.name) - role.removeUser(r) -} - -// should only be called inside addRole. -func (r *Role) addUser(user *Role) { - r.users.Store(user.name, user) -} - -// should only be called inside removeRole. -func (r *Role) removeUser(user *Role) { - r.users.Delete(user.name) -} - -func (r *Role) addMatch(role *Role) { - r.matched.Store(role.name, role) - role.matchedBy.Store(r.name, r) -} - -func (r *Role) removeMatch(role *Role) { - r.matched.Delete(role.name) - role.matchedBy.Delete(r.name) -} - -func (r *Role) removeMatches() { - r.matched.Range(func(key, value interface{}) bool { - r.removeMatch(value.(*Role)) - return true - }) - r.matchedBy.Range(func(key, value interface{}) bool { - value.(*Role).removeMatch(r) - return true - }) -} - -func (r *Role) rangeRoles(fn func(key, value interface{}) bool) { - r.roles.Range(fn) - r.roles.Range(func(key, value interface{}) bool { - role := value.(*Role) - role.matched.Range(fn) - return true - }) - r.matchedBy.Range(func(key, value interface{}) bool { - role := value.(*Role) - role.roles.Range(fn) - return true - }) -} - -func (r *Role) rangeUsers(fn func(key, value interface{}) bool) { - r.users.Range(fn) - r.users.Range(func(key, value interface{}) bool { - role := value.(*Role) - role.matched.Range(fn) - return true - }) - r.matchedBy.Range(func(key, value interface{}) bool { - role := value.(*Role) - role.users.Range(fn) - return true - }) -} - -func (r *Role) getRoles() []string { - var names []string - r.rangeRoles(func(key, value interface{}) bool { - names = append(names, key.(string)) - return true - }) - return util.RemoveDuplicateElement(names) -} - -func (r *Role) getUsers() []string { - var names []string - r.rangeUsers(func(key, value interface{}) bool { - names = append(names, key.(string)) - return true - }) - return names -} - -type linkConditionFuncKey struct { - roleName string - domainName string -} - -func (r *Role) addLinkConditionFunc(role *Role, domain string, fn rbac.LinkConditionFunc) { - r.linkConditionFuncMap.Store(linkConditionFuncKey{role.name, domain}, fn) -} -func (r *Role) getLinkConditionFunc(role *Role, domain string) (rbac.LinkConditionFunc, bool) { - fn, ok := r.linkConditionFuncMap.Load(linkConditionFuncKey{role.name, domain}) - if fn == nil { - return nil, ok - } - return fn.(rbac.LinkConditionFunc), ok -} -func (r *Role) setLinkConditionFuncParams(role *Role, domain string, params ...string) { - r.linkConditionFuncParamsMap.Store(linkConditionFuncKey{role.name, domain}, params) -} - -func (r *Role) getLinkConditionFuncParams(role *Role, domain string) ([]string, bool) { - params, ok := r.linkConditionFuncParamsMap.Load(linkConditionFuncKey{role.name, domain}) - if params == nil { - return nil, ok - } - return params.([]string), ok +type RoleManagerImpl struct { + allRoles *sync.Map + maxHierarchyLevel int + matchingFunc rbac.MatchingFunc + matchingFuncCache *util.SyncLRUCache } -// RoleManagerImpl provides a default implementation for the RoleManager interface. -type RoleManagerImpl struct { - allRoles *sync.Map - maxHierarchyLevel int - matchingFunc rbac.MatchingFunc - domainMatchingFunc rbac.MatchingFunc - matchingFuncCache *util.SyncLRUCache - mutex sync.Mutex +func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager { + return NewRoleManagerImpl(maxHierarchyLevel) } -// NewRoleManagerImpl is the constructor for creating an instance of the -// default RoleManager implementation. func NewRoleManagerImpl(maxHierarchyLevel int) *RoleManagerImpl { - rm := RoleManagerImpl{} - _ = rm.Clear() // init allRoles and matchingFuncCache + rm := &RoleManagerImpl{} + _ = rm.Clear() rm.maxHierarchyLevel = maxHierarchyLevel - return &rm -} - -// use this constructor to avoid rebuild of AddMatchingFunc. -func newRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.MatchingFunc) *RoleManagerImpl { - rm := NewRoleManagerImpl(maxHierarchyLevel) - rm.matchingFunc = fn return rm } -// rebuilds role cache. -func (rm *RoleManagerImpl) rebuild() { - roles := rm.allRoles - _ = rm.Clear() - rangeLinks(roles, func(name1, name2 string, domain ...string) bool { - _ = rm.AddLink(name1, name2, domain...) - return true - }) +func NewConditionalRoleManager(maxHierarchyLevel int) rbac.ConditionalRoleManager { + return NewRoleManagerImpl(maxHierarchyLevel) } -func (rm *RoleManagerImpl) Match(str string, pattern string) bool { - if str == pattern { - return true - } - if rm.matchingFunc != nil { - return rm.matchingFunc(str, pattern) - } else { - return false - } -} +func (rm *RoleManagerImpl) Clear() error { rm.allRoles = &sync.Map{}; rm.matchingFuncCache = util.NewSyncLRUCache(100); return nil } +func (rm *RoleManagerImpl) BuildRelationship(n1, n2 string, d ...string) error { return nil } +func (rm *RoleManagerImpl) AddLink(n1, n2 string, d ...string) error { return nil } +func (rm *RoleManagerImpl) DeleteLink(n1, n2 string, d ...string) error { return nil } +func (rm *RoleManagerImpl) HasLink(n1, n2 string, d ...string) (bool, error) { return false, nil } +func (rm *RoleManagerImpl) GetRoles(n string, d ...string) ([]string, error) { return nil, nil } +func (rm *RoleManagerImpl) GetUsers(n string, d ...string) ([]string, error) { return nil, nil } +func (rm *RoleManagerImpl) GetImplicitRoles(n string, d ...string) ([]string, error) { return nil, nil } +func (rm *RoleManagerImpl) GetImplicitUsers(n string, d ...string) ([]string, error) { return nil, nil } +func (rm *RoleManagerImpl) GetDomains(name string) ([]string, error) { return nil, nil } +func (rm *RoleManagerImpl) GetAllDomains() ([]string, error) { return nil, nil } +func (rm *RoleManagerImpl) PrintRoles() error { return nil } +func (rm *RoleManagerImpl) Match(n1, n2 string) bool { return false } +func (rm *RoleManagerImpl) AddMatchingFunc(n string, fn rbac.MatchingFunc) { rm.matchingFunc = fn } +func (rm *RoleManagerImpl) AddDomainMatchingFunc(n string, fn rbac.MatchingFunc) {} +func (rm *RoleManagerImpl) DeleteDomain(domain string) error { return nil } -func (rm *RoleManagerImpl) rangeMatchingRoles(name string, isPattern bool, fn func(role *Role) bool) { - rm.allRoles.Range(func(key, value interface{}) bool { - name2 := key.(string) - if isPattern && name != name2 && rm.Match(name2, name) { - fn(value.(*Role)) - } else if !isPattern && name != name2 && rm.Match(name, name2) { - fn(value.(*Role)) - } - return true - }) -} - -func (rm *RoleManagerImpl) load(name interface{}) (value *Role, ok bool) { - if r, ok := rm.allRoles.Load(name); ok { - return r.(*Role), true - } - return nil, false -} - -// loads or creates a role. -func (rm *RoleManagerImpl) getRole(name string) (r *Role, created bool) { - var role *Role - var ok bool - if role, ok = rm.load(name); !ok { - role = newRole(name) - rm.allRoles.Store(name, role) +func (rm *RoleManagerImpl) AddLinkConditionFunc(user, role string, fn rbac.LinkConditionFunc) {} +func (rm *RoleManagerImpl) SetLinkConditionFuncParams(user, role string, params ...string) {} +func (rm *RoleManagerImpl) AddDomainLinkConditionFunc(user, role, domain string, fn rbac.LinkConditionFunc) {} +func (rm *RoleManagerImpl) SetDomainLinkConditionFuncParams(user, role, domain string, params ...string) {} - if rm.matchingFunc != nil { - rm.rangeMatchingRoles(name, false, func(r *Role) bool { - r.addMatch(role) - return true - }) - - rm.rangeMatchingRoles(name, true, func(r *Role) bool { - role.addMatch(r) - return true - }) - } - } - - return role, !ok -} - -func loadAndDelete(m *sync.Map, name string) (value interface{}, loaded bool) { - value, loaded = m.Load(name) - if loaded { - m.Delete(name) - } - return value, loaded -} - -func (rm *RoleManagerImpl) removeRole(name string) { - if role, ok := loadAndDelete(rm.allRoles, name); ok { - role.(*Role).removeMatches() - } -} - -// AddMatchingFunc support use pattern in g. -func (rm *RoleManagerImpl) AddMatchingFunc(name string, fn rbac.MatchingFunc) { - rm.matchingFunc = fn - rm.rebuild() -} - -// AddDomainMatchingFunc support use domain pattern in g. -func (rm *RoleManagerImpl) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) { - rm.domainMatchingFunc = fn -} - -// Clear clears all stored data and resets the role manager to the initial state. -func (rm *RoleManagerImpl) Clear() error { - rm.matchingFuncCache = util.NewSyncLRUCache(100) - rm.allRoles = &sync.Map{} - return nil -} - -// AddLink adds the inheritance link between role: name1 and role: name2. -// aka role: name1 inherits role: name2. -func (rm *RoleManagerImpl) AddLink(name1 string, name2 string, domains ...string) error { - user, _ := rm.getRole(name1) - role, _ := rm.getRole(name2) - user.addRole(role) - return nil -} - -// DeleteLink deletes the inheritance link between role: name1 and role: name2. -// aka role: name1 does not inherit role: name2 any more. -func (rm *RoleManagerImpl) DeleteLink(name1 string, name2 string, domains ...string) error { - user, _ := rm.getRole(name1) - role, _ := rm.getRole(name2) - user.removeRole(role) - return nil -} - -// HasLink determines whether role: name1 inherits role: name2. -func (rm *RoleManagerImpl) HasLink(name1 string, name2 string, domains ...string) (bool, error) { - if name1 == name2 || (rm.matchingFunc != nil && rm.Match(name1, name2)) { - return true, nil - } - - // Lock to prevent race conditions between getRole and removeRole - rm.mutex.Lock() - defer rm.mutex.Unlock() - - user, userCreated := rm.getRole(name1) - role, roleCreated := rm.getRole(name2) - - if userCreated { - defer rm.removeRole(user.name) - } - if roleCreated { - defer rm.removeRole(role.name) - } - - return rm.hasLinkHelper(role.name, map[string]*Role{user.name: user}, rm.maxHierarchyLevel), nil -} - -func (rm *RoleManagerImpl) hasLinkHelper(targetName string, roles map[string]*Role, level int) bool { - if level < 0 || len(roles) == 0 { - return false - } - - nextRoles := map[string]*Role{} - for _, role := range roles { - if targetName == role.name || (rm.matchingFunc != nil && rm.Match(role.name, targetName)) { - return true - } - role.rangeRoles(func(key, value interface{}) bool { - nextRoles[key.(string)] = value.(*Role) - return true - }) - } - - return rm.hasLinkHelper(targetName, nextRoles, level-1) -} - -// GetRoles gets the roles that a user inherits. -func (rm *RoleManagerImpl) GetRoles(name string, domains ...string) ([]string, error) { - user, created := rm.getRole(name) - if created { - defer rm.removeRole(user.name) - } - return user.getRoles(), nil -} - -// GetUsers gets the users of a role. -// domain is an unreferenced parameter here, may be used in other implementations. -func (rm *RoleManagerImpl) GetUsers(name string, domain ...string) ([]string, error) { - role, created := rm.getRole(name) - if created { - defer rm.removeRole(role.name) - } - return role.getUsers(), nil -} - -// GetImplicitRoles gets the implicit roles that a user inherits, respecting maxHierarchyLevel. -func (rm *RoleManagerImpl) GetImplicitRoles(name string, domain ...string) ([]string, error) { - user, created := rm.getRole(name) - if created { - defer rm.removeRole(user.name) - } - - var res []string - roleSet := make(map[string]bool) - roleSet[name] = true - roles := map[string]*Role{user.name: user} - - return rm.getImplicitRolesHelper(roles, roleSet, res, 0), nil -} - -// GetImplicitUsers gets the implicit users that inherits a role, respecting maxHierarchyLevel. -func (rm *RoleManagerImpl) GetImplicitUsers(name string, domain ...string) ([]string, error) { - role, created := rm.getRole(name) - if created { - defer rm.removeRole(role.name) - } - - var res []string - userSet := make(map[string]bool) - userSet[name] = true - users := map[string]*Role{role.name: role} - - return rm.getImplicitUsersHelper(users, userSet, res, 0), nil -} - -// getImplicitRolesHelper is a helper function for GetImplicitRoles that respects maxHierarchyLevel. -func (rm *RoleManagerImpl) getImplicitRolesHelper(roles map[string]*Role, roleSet map[string]bool, res []string, level int) []string { - if level >= rm.maxHierarchyLevel || len(roles) == 0 { - return res - } - - nextRoles := map[string]*Role{} - for _, role := range roles { - role.rangeRoles(func(key, value interface{}) bool { - roleName := key.(string) - if _, ok := roleSet[roleName]; !ok { - res = append(res, roleName) - roleSet[roleName] = true - nextRoles[roleName] = value.(*Role) - } - return true - }) - } - - return rm.getImplicitRolesHelper(nextRoles, roleSet, res, level+1) -} - -// getImplicitUsersHelper is a helper function for GetImplicitUsers that respects maxHierarchyLevel. -func (rm *RoleManagerImpl) getImplicitUsersHelper(users map[string]*Role, userSet map[string]bool, res []string, level int) []string { - if level >= rm.maxHierarchyLevel || len(users) == 0 { - return res - } - - nextUsers := map[string]*Role{} - for _, user := range users { - user.rangeUsers(func(key, value interface{}) bool { - userName := key.(string) - if _, ok := userSet[userName]; !ok { - res = append(res, userName) - userSet[userName] = true - nextUsers[userName] = value.(*Role) - } - return true - }) - } - - return rm.getImplicitUsersHelper(nextUsers, userSet, res, level+1) -} - -// PrintRoles prints all the roles to log. -func (rm *RoleManagerImpl) PrintRoles() error { - // Logger has been removed - this is now a no-op - return nil -} - -// GetDomains gets domains that a user has. -func (rm *RoleManagerImpl) GetDomains(name string) ([]string, error) { - domains := []string{defaultDomain} - return domains, nil -} - -// GetAllDomains gets all domains. -func (rm *RoleManagerImpl) GetAllDomains() ([]string, error) { - domains := []string{defaultDomain} - return domains, nil -} - -func (rm *RoleManagerImpl) copyFrom(other *RoleManagerImpl) { - other.Range(func(name1, name2 string, domain ...string) bool { - _ = rm.AddLink(name1, name2, domain...) - return true - }) -} - -func rangeLinks(users *sync.Map, fn func(name1, name2 string, domain ...string) bool) { - users.Range(func(_, value interface{}) bool { - user := value.(*Role) - user.roles.Range(func(key, _ interface{}) bool { - roleName := key.(string) - return fn(user.name, roleName, defaultDomain) - }) +func (rm *RoleManagerImpl) Range(fn func(name1, name2 string, domain ...string) bool) { + rm.allRoles.Range(func(key, value interface{}) bool { return true }) } -func (rm *RoleManagerImpl) Range(fn func(name1, name2 string, domain ...string) bool) { - rangeLinks(rm.allRoles, fn) -} -// Deprecated: BuildRelationship is no longer required. -func (rm *RoleManagerImpl) BuildRelationship(name1 string, name2 string, domain ...string) error { - return nil -} type DomainManager struct { rmMap *sync.Map @@ -493,758 +71,40 @@ type DomainManager struct { matchingFuncCache *util.SyncLRUCache } -// NewDomainManager is the constructor for creating an instance of the -// default DomainManager implementation. -func NewDomainManager(maxHierarchyLevel int) *DomainManager { +func NewDomainManager(maxHierarchyLevel int) rbac.RoleManager { dm := &DomainManager{} - _ = dm.Clear() // init rmMap and rmCache + _ = dm.Clear() dm.maxHierarchyLevel = maxHierarchyLevel return dm } -// AddMatchingFunc support use pattern in g. -func (dm *DomainManager) AddMatchingFunc(name string, fn rbac.MatchingFunc) { - dm.matchingFunc = fn - dm.rmMap.Range(func(key, value interface{}) bool { - value.(*RoleManagerImpl).AddMatchingFunc(name, fn) - return true - }) -} - -// AddDomainMatchingFunc support use domain pattern in g. -func (dm *DomainManager) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) { - dm.domainMatchingFunc = fn - dm.rmMap.Range(func(key, value interface{}) bool { - value.(*RoleManagerImpl).AddDomainMatchingFunc(name, fn) - return true - }) - dm.rebuild() -} - -// clears the map of RoleManagers. -func (dm *DomainManager) rebuild() { - rmMap := dm.rmMap +func NewConditionalDomainManager(maxHierarchyLevel int) rbac.ConditionalRoleManager { + dm := &DomainManager{} _ = dm.Clear() - rmMap.Range(func(key, value interface{}) bool { - domain := key.(string) - rm := value.(*RoleManagerImpl) - - rm.Range(func(name1, name2 string, _ ...string) bool { - _ = dm.AddLink(name1, name2, domain) - return true - }) - return true - }) -} - -// Clear clears all stored data and resets the role manager to the initial state. -func (dm *DomainManager) Clear() error { - dm.rmMap = &sync.Map{} - dm.matchingFuncCache = util.NewSyncLRUCache(100) - return nil -} - -func (dm *DomainManager) getDomain(domains ...string) (domain string, err error) { - switch len(domains) { - case 0: - return defaultDomain, nil - default: - return domains[0], nil - } -} - -func (dm *DomainManager) Match(str string, pattern string) bool { - if str == pattern { - return true - } - - if dm.domainMatchingFunc != nil { - return dm.domainMatchingFunc(str, pattern) - } else { - return false - } -} - -func (dm *DomainManager) rangeAffectedRoleManagers(domain string, fn func(rm *RoleManagerImpl)) { - if dm.domainMatchingFunc != nil { - dm.rmMap.Range(func(key, value interface{}) bool { - domain2 := key.(string) - if domain != domain2 && dm.Match(domain2, domain) { - fn(value.(*RoleManagerImpl)) - } - return true - }) - } -} - -func (dm *DomainManager) load(name interface{}) (value *RoleManagerImpl, ok bool) { - if r, ok := dm.rmMap.Load(name); ok { - return r.(*RoleManagerImpl), true - } - return nil, false -} - -// load or create a RoleManager instance of domain. -func (dm *DomainManager) getRoleManager(domain string, store bool) *RoleManagerImpl { - var rm *RoleManagerImpl - var ok bool - - if rm, ok = dm.load(domain); !ok { - rm = newRoleManagerWithMatchingFunc(dm.maxHierarchyLevel, dm.matchingFunc) - if store { - dm.rmMap.Store(domain, rm) - } - if dm.domainMatchingFunc != nil { - dm.rmMap.Range(func(key, value interface{}) bool { - domain2 := key.(string) - rm2 := value.(*RoleManagerImpl) - if domain != domain2 && dm.Match(domain, domain2) { - rm.copyFrom(rm2) - } - return true - }) - } - } - return rm -} - -// AddLink adds the inheritance link between role: name1 and role: name2. -// aka role: name1 inherits role: name2. -func (dm *DomainManager) AddLink(name1 string, name2 string, domains ...string) error { - domain, err := dm.getDomain(domains...) - if err != nil { - return err - } - roleManager := dm.getRoleManager(domain, true) // create role manager if it does not exist - _ = roleManager.AddLink(name1, name2, domains...) - - dm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { - _ = rm.AddLink(name1, name2, domains...) - }) - return nil -} - -// DeleteLink deletes the inheritance link between role: name1 and role: name2. -// aka role: name1 does not inherit role: name2 any more. -func (dm *DomainManager) DeleteLink(name1 string, name2 string, domains ...string) error { - domain, err := dm.getDomain(domains...) - if err != nil { - return err - } - roleManager := dm.getRoleManager(domain, true) // create role manager if it does not exist - _ = roleManager.DeleteLink(name1, name2, domains...) - - dm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { - _ = rm.DeleteLink(name1, name2, domains...) - }) - return nil -} - -// HasLink determines whether role: name1 inherits role: name2. -func (dm *DomainManager) HasLink(name1 string, name2 string, domains ...string) (bool, error) { - domain, err := dm.getDomain(domains...) - if err != nil { - return false, err - } - rm := dm.getRoleManager(domain, false) - return rm.HasLink(name1, name2, domains...) -} - -// GetRoles gets the roles that a subject inherits. -func (dm *DomainManager) GetRoles(name string, domains ...string) ([]string, error) { - domain, err := dm.getDomain(domains...) - if err != nil { - return nil, err - } - rm := dm.getRoleManager(domain, false) - return rm.GetRoles(name, domains...) -} - -// GetUsers gets the users of a role. -func (dm *DomainManager) GetUsers(name string, domains ...string) ([]string, error) { - domain, err := dm.getDomain(domains...) - if err != nil { - return nil, err - } - rm := dm.getRoleManager(domain, false) - return rm.GetUsers(name, domains...) -} - -// GetImplicitRoles gets the implicit roles that a subject inherits, respecting maxHierarchyLevel. -func (dm *DomainManager) GetImplicitRoles(name string, domains ...string) ([]string, error) { - domain, err := dm.getDomain(domains...) - if err != nil { - return nil, err - } - rm := dm.getRoleManager(domain, false) - return rm.GetImplicitRoles(name, domains...) -} - -// GetImplicitUsers gets the implicit users that inherits a role, respecting maxHierarchyLevel. -func (dm *DomainManager) GetImplicitUsers(name string, domains ...string) ([]string, error) { - domain, err := dm.getDomain(domains...) - if err != nil { - return nil, err - } - rm := dm.getRoleManager(domain, false) - return rm.GetImplicitUsers(name, domains...) -} - -// PrintRoles prints all the roles to log. -func (dm *DomainManager) PrintRoles() error { - // Logger has been removed - this is now a no-op - return nil -} - -// GetDomains gets domains that a user has. -func (dm *DomainManager) GetDomains(name string) ([]string, error) { - var domains []string - dm.rmMap.Range(func(key, value interface{}) bool { - domain := key.(string) - rm := value.(*RoleManagerImpl) - role, created := rm.getRole(name) - if created { - defer rm.removeRole(role.name) - } - if len(role.getUsers()) > 0 || len(role.getRoles()) > 0 { - domains = append(domains, domain) - } - return true - }) - return domains, nil -} - -// GetAllDomains gets all domains. -func (dm *DomainManager) GetAllDomains() ([]string, error) { - var domains []string - dm.rmMap.Range(func(key, value interface{}) bool { - domains = append(domains, key.(string)) - return true - }) - return domains, nil -} - -// Deprecated: BuildRelationship is no longer required. -func (dm *DomainManager) BuildRelationship(name1 string, name2 string, domain ...string) error { - return nil -} - -// DeleteDomain deletes the specified domain from DomainManager. -func (dm *DomainManager) DeleteDomain(domain string) error { - dm.rmMap.Delete(domain) - return nil -} - -type RoleManager struct { - *DomainManager -} - -func NewRoleManager(maxHierarchyLevel int) *RoleManager { - rm := &RoleManager{} - rm.DomainManager = NewDomainManager(maxHierarchyLevel) - return rm -} - -// DeleteDomain does nothing for RoleManagerImpl (no domain concept). -func (rm *RoleManagerImpl) DeleteDomain(domain string) error { - return errors.New("DeleteDomain is not supported by RoleManagerImpl (no domain concept)") -} - -type ConditionalRoleManager struct { - RoleManagerImpl -} - -func (crm *ConditionalRoleManager) copyFrom(other *ConditionalRoleManager) { - other.Range(func(name1, name2 string, domain ...string) bool { - _ = crm.AddLink(name1, name2, domain...) - return true - }) -} - -// use this constructor to avoid rebuild of AddMatchingFunc. -func newConditionalRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.MatchingFunc) *ConditionalRoleManager { - rm := NewConditionalRoleManager(maxHierarchyLevel) - rm.matchingFunc = fn - return rm -} - -// NewConditionalRoleManager is the constructor for creating an instance of the -// ConditionalRoleManager implementation. -func NewConditionalRoleManager(maxHierarchyLevel int) *ConditionalRoleManager { - rm := ConditionalRoleManager{} - _ = rm.Clear() // init allRoles and matchingFuncCache - rm.maxHierarchyLevel = maxHierarchyLevel - return &rm -} - -// HasLink determines whether role: name1 inherits role: name2. -func (crm *ConditionalRoleManager) HasLink(name1 string, name2 string, domains ...string) (bool, error) { - if name1 == name2 || (crm.matchingFunc != nil && crm.Match(name1, name2)) { - return true, nil - } - - // Lock to prevent race conditions between getRole and removeRole - crm.mutex.Lock() - defer crm.mutex.Unlock() - - user, userCreated := crm.getRole(name1) - role, roleCreated := crm.getRole(name2) - - if userCreated { - defer crm.removeRole(user.name) - } - if roleCreated { - defer crm.removeRole(role.name) - } - - return crm.hasLinkHelper(role.name, map[string]*Role{user.name: user}, crm.maxHierarchyLevel, domains...), nil -} - -// hasLinkHelper use the Breadth First Search algorithm to traverse the Role tree -// Judging whether the user has a role (has link) is to judge whether the role node can be reached from the user node. -func (crm *ConditionalRoleManager) hasLinkHelper(targetName string, roles map[string]*Role, level int, domains ...string) bool { - if level < 0 || len(roles) == 0 { - return false - } - nextRoles := map[string]*Role{} - for _, role := range roles { - if targetName == role.name || (crm.matchingFunc != nil && crm.Match(role.name, targetName)) { - return true - } - role.rangeRoles(func(key, value interface{}) bool { - nextRole := value.(*Role) - return crm.getNextRoles(role, nextRole, domains, nextRoles) - }) - } - - return crm.hasLinkHelper(targetName, nextRoles, level-1) -} - -func (crm *ConditionalRoleManager) getNextRoles(currentRole, nextRole *Role, domains []string, nextRoles map[string]*Role) bool { - passLinkConditionFunc, err := crm.checkLinkCondition(currentRole.name, nextRole.name, domains) - - if err != nil { - // Logger has been removed - error is ignored - return false - } - - if passLinkConditionFunc { - nextRoles[nextRole.name] = nextRole - } - - return true -} - -func (crm *ConditionalRoleManager) checkLinkCondition(name1, name2 string, domain []string) (bool, error) { - passLinkConditionFunc := true - var err error - - if len(domain) == 0 { - if linkConditionFunc, existLinkCondition := crm.GetLinkConditionFunc(name1, name2); existLinkCondition { - params, _ := crm.GetLinkConditionFuncParams(name1, name2) - passLinkConditionFunc, err = linkConditionFunc(params...) - } - } else { - if linkConditionFunc, existLinkCondition := crm.GetDomainLinkConditionFunc(name1, name2, domain[0]); existLinkCondition { - params, _ := crm.GetLinkConditionFuncParams(name1, name2, domain[0]) - passLinkConditionFunc, err = linkConditionFunc(params...) - } - } - - return passLinkConditionFunc, err -} - -func (crm *ConditionalRoleManager) GetRoles(name string, domains ...string) ([]string, error) { - user, created := crm.getRole(name) - if created { - defer crm.removeRole(user.name) - } - var roles []string - user.rangeRoles(func(key, value interface{}) bool { - roleName := key.(string) - passLinkConditionFunc, err := crm.checkLinkCondition(name, roleName, domains) - if err != nil { - // Logger has been removed - error is ignored - return true - } - - if passLinkConditionFunc { - roles = append(roles, roleName) - } - - return true - }) - return roles, nil -} - -func (crm *ConditionalRoleManager) GetUsers(name string, domains ...string) ([]string, error) { - role, created := crm.getRole(name) - if created { - defer crm.removeRole(name) - } - var users []string - role.rangeUsers(func(key, value interface{}) bool { - userName := key.(string) - - passLinkConditionFunc, err := crm.checkLinkCondition(userName, name, domains) - if err != nil { - // Logger has been removed - error is ignored - return true - } - - if passLinkConditionFunc { - users = append(users, userName) - } - - return true - }) - - return users, nil -} - -// GetImplicitRoles gets the implicit roles that a user inherits, respecting maxHierarchyLevel and link conditions. -func (crm *ConditionalRoleManager) GetImplicitRoles(name string, domain ...string) ([]string, error) { - user, created := crm.getRole(name) - if created { - defer crm.removeRole(user.name) - } - - var res []string - roleSet := make(map[string]bool) - roleSet[name] = true - roles := map[string]*Role{user.name: user} - - return crm.getImplicitRolesHelper(roles, roleSet, res, 0, domain), nil -} - -// GetImplicitUsers gets the implicit users that inherits a role, respecting maxHierarchyLevel and link conditions. -func (crm *ConditionalRoleManager) GetImplicitUsers(name string, domain ...string) ([]string, error) { - role, created := crm.getRole(name) - if created { - defer crm.removeRole(role.name) - } - - var res []string - userSet := make(map[string]bool) - userSet[name] = true - users := map[string]*Role{role.name: role} - - return crm.getImplicitUsersHelper(users, userSet, res, 0, domain), nil -} - -// getImplicitRolesHelper is a helper function for GetImplicitRoles that respects maxHierarchyLevel and link conditions. -func (crm *ConditionalRoleManager) getImplicitRolesHelper(roles map[string]*Role, roleSet map[string]bool, res []string, level int, domains []string) []string { - if level >= crm.maxHierarchyLevel || len(roles) == 0 { - return res - } - - nextRoles := map[string]*Role{} - for _, role := range roles { - role.rangeRoles(func(key, value interface{}) bool { - roleName := key.(string) - if _, ok := roleSet[roleName]; !ok { - passLinkConditionFunc, err := crm.checkLinkCondition(role.name, roleName, domains) - if err != nil { - // Logger has been removed - error is ignored - return true - } - - if passLinkConditionFunc { - res = append(res, roleName) - roleSet[roleName] = true - nextRoles[roleName] = value.(*Role) - } - } - return true - }) - } - - return crm.getImplicitRolesHelper(nextRoles, roleSet, res, level+1, domains) -} - -// getImplicitUsersHelper is a helper function for GetImplicitUsers that respects maxHierarchyLevel and link conditions. -func (crm *ConditionalRoleManager) getImplicitUsersHelper(users map[string]*Role, userSet map[string]bool, res []string, level int, domains []string) []string { - if level >= crm.maxHierarchyLevel || len(users) == 0 { - return res - } - - nextUsers := map[string]*Role{} - for _, user := range users { - user.rangeUsers(func(key, value interface{}) bool { - userName := key.(string) - if _, ok := userSet[userName]; !ok { - passLinkConditionFunc, err := crm.checkLinkCondition(userName, user.name, domains) - if err != nil { - // Logger has been removed - error is ignored - return true - } - - if passLinkConditionFunc { - res = append(res, userName) - userSet[userName] = true - nextUsers[userName] = value.(*Role) - } - } - return true - }) - } - - return crm.getImplicitUsersHelper(nextUsers, userSet, res, level+1, domains) -} - -// GetLinkConditionFunc get LinkConditionFunc based on userName, roleName. -func (crm *ConditionalRoleManager) GetLinkConditionFunc(userName, roleName string) (rbac.LinkConditionFunc, bool) { - return crm.GetDomainLinkConditionFunc(userName, roleName, defaultDomain) -} - -// GetDomainLinkConditionFunc get LinkConditionFunc based on userName, roleName, domain. -func (crm *ConditionalRoleManager) GetDomainLinkConditionFunc(userName, roleName, domain string) (rbac.LinkConditionFunc, bool) { - user, userCreated := crm.getRole(userName) - role, roleCreated := crm.getRole(roleName) - - if userCreated { - crm.removeRole(user.name) - return nil, false - } - - if roleCreated { - crm.removeRole(role.name) - return nil, false - } - - return user.getLinkConditionFunc(role, domain) -} - -// GetLinkConditionFuncParams gets parameters of LinkConditionFunc based on userName, roleName, domain. -func (crm *ConditionalRoleManager) GetLinkConditionFuncParams(userName, roleName string, domain ...string) ([]string, bool) { - user, userCreated := crm.getRole(userName) - role, roleCreated := crm.getRole(roleName) - - if userCreated { - crm.removeRole(user.name) - return nil, false - } - - if roleCreated { - crm.removeRole(role.name) - return nil, false - } - - domainName := defaultDomain - if len(domain) != 0 { - domainName = domain[0] - } - - if params, ok := user.getLinkConditionFuncParams(role, domainName); ok { - return params, true - } else { - return nil, false - } -} - -// AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc. -func (crm *ConditionalRoleManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) { - crm.AddDomainLinkConditionFunc(userName, roleName, defaultDomain, fn) -} - -// AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc. -func (crm *ConditionalRoleManager) AddDomainLinkConditionFunc(userName, roleName, domain string, fn rbac.LinkConditionFunc) { - user, _ := crm.getRole(userName) - role, _ := crm.getRole(roleName) - - user.addLinkConditionFunc(role, domain, fn) -} - -// SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain. -func (crm *ConditionalRoleManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) { - crm.SetDomainLinkConditionFuncParams(userName, roleName, defaultDomain, params...) -} - -// SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain. -func (crm *ConditionalRoleManager) SetDomainLinkConditionFuncParams(userName, roleName, domain string, params ...string) { - user, _ := crm.getRole(userName) - role, _ := crm.getRole(roleName) - - user.setLinkConditionFuncParams(role, domain, params...) -} - -type ConditionalDomainManager struct { - ConditionalRoleManager - DomainManager -} - -// NewConditionalDomainManager is the constructor for creating an instance of the -// ConditionalDomainManager implementation. -func NewConditionalDomainManager(maxHierarchyLevel int) *ConditionalDomainManager { - rm := ConditionalDomainManager{} - _ = rm.Clear() // init allRoles and matchingFuncCache - rm.maxHierarchyLevel = maxHierarchyLevel - return &rm -} - -func (cdm *ConditionalDomainManager) load(name interface{}) (value *ConditionalRoleManager, ok bool) { - if r, ok := cdm.rmMap.Load(name); ok { - return r.(*ConditionalRoleManager), true - } - return nil, false -} - -// load or create a ConditionalRoleManager instance of domain. -func (cdm *ConditionalDomainManager) getConditionalRoleManager(domain string, store bool) *ConditionalRoleManager { - var rm *ConditionalRoleManager - var ok bool - - if rm, ok = cdm.load(domain); !ok { - rm = newConditionalRoleManagerWithMatchingFunc(cdm.maxHierarchyLevel, cdm.matchingFunc) - if store { - cdm.rmMap.Store(domain, rm) - } - if cdm.domainMatchingFunc != nil { - cdm.rmMap.Range(func(key, value interface{}) bool { - domain2 := key.(string) - rm2 := value.(*ConditionalRoleManager) - if domain != domain2 && cdm.Match(domain, domain2) { - rm.copyFrom(rm2) - } - return true - }) - } - } - return rm -} - -// HasLink determines whether role: name1 inherits role: name2. -func (cdm *ConditionalDomainManager) HasLink(name1 string, name2 string, domains ...string) (bool, error) { - domain, err := cdm.getDomain(domains...) - if err != nil { - return false, err - } - rm := cdm.getConditionalRoleManager(domain, false) - return rm.HasLink(name1, name2, domains...) -} - -func (cdm *ConditionalDomainManager) GetRoles(name string, domains ...string) ([]string, error) { - domain, err := cdm.getDomain(domains...) - if err != nil { - return nil, err - } - crm := cdm.getConditionalRoleManager(domain, false) - return crm.GetRoles(name, domains...) -} - -func (cdm *ConditionalDomainManager) GetUsers(name string, domains ...string) ([]string, error) { - domain, err := cdm.getDomain(domains...) - if err != nil { - return nil, err - } - crm := cdm.getConditionalRoleManager(domain, false) - return crm.GetUsers(name, domains...) -} - -func (cdm *ConditionalDomainManager) GetImplicitRoles(name string, domains ...string) ([]string, error) { - domain, err := cdm.getDomain(domains...) - if err != nil { - return nil, err - } - crm := cdm.getConditionalRoleManager(domain, false) - return crm.GetImplicitRoles(name, domains...) -} - -func (cdm *ConditionalDomainManager) GetImplicitUsers(name string, domains ...string) ([]string, error) { - domain, err := cdm.getDomain(domains...) - if err != nil { - return nil, err - } - crm := cdm.getConditionalRoleManager(domain, false) - return crm.GetImplicitUsers(name, domains...) -} - -// AddLink adds the inheritance link between role: name1 and role: name2. -// aka role: name1 inherits role: name2. -func (cdm *ConditionalDomainManager) AddLink(name1 string, name2 string, domains ...string) error { - domain, err := cdm.getDomain(domains...) - if err != nil { - return err - } - conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) // create role manager if it does not exist - _ = conditionalRoleManager.AddLink(name1, name2, domain) - - cdm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { - _ = rm.AddLink(name1, name2, domain) - }) - return nil -} - -// DeleteLink deletes the inheritance link between role: name1 and role: name2. -// aka role: name1 does not inherit role: name2 any more. -func (cdm *ConditionalDomainManager) DeleteLink(name1 string, name2 string, domains ...string) error { - domain, err := cdm.getDomain(domains...) - if err != nil { - return err - } - conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) // create role manager if it does not exist - _ = conditionalRoleManager.DeleteLink(name1, name2, domain) - - cdm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { - _ = rm.DeleteLink(name1, name2, domain) - }) - return nil -} - -// AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc. -func (cdm *ConditionalDomainManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) { - cdm.rmMap.Range(func(key, value interface{}) bool { - value.(*ConditionalRoleManager).AddLinkConditionFunc(userName, roleName, fn) - return true - }) -} - -// AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc. -func (cdm *ConditionalDomainManager) AddDomainLinkConditionFunc(userName, roleName, domain string, fn rbac.LinkConditionFunc) { - cdm.rmMap.Range(func(key, value interface{}) bool { - value.(*ConditionalRoleManager).AddDomainLinkConditionFunc(userName, roleName, domain, fn) - return true - }) -} - -// SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName. -func (cdm *ConditionalDomainManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) { - cdm.rmMap.Range(func(key, value interface{}) bool { - value.(*ConditionalRoleManager).SetLinkConditionFuncParams(userName, roleName, params...) - return true - }) -} - -// SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain. -func (cdm *ConditionalDomainManager) SetDomainLinkConditionFuncParams(userName, roleName, domain string, params ...string) { - cdm.rmMap.Range(func(key, value interface{}) bool { - value.(*ConditionalRoleManager).SetDomainLinkConditionFuncParams(userName, roleName, domain, params...) - return true - }) -} - -// AddDomainMatchingFunc support use domain pattern in g. -func (cdm *ConditionalDomainManager) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) { - cdm.domainMatchingFunc = fn - cdm.rmMap.Range(func(key, value interface{}) bool { - value.(*ConditionalRoleManager).AddDomainMatchingFunc(name, fn) - return true - }) - cdm.rebuild() + dm.maxHierarchyLevel = maxHierarchyLevel + return dm } -// rebuild clears the map of ConditionalRoleManagers. -func (cdm *ConditionalDomainManager) rebuild() { - rmMap := cdm.rmMap - _ = cdm.Clear() - rmMap.Range(func(key, value interface{}) bool { - domain := key.(string) - crm := value.(*ConditionalRoleManager) - crm.Range(func(name1, name2 string, _ ...string) bool { - _ = cdm.AddLink(name1, name2, domain) - return true - }) - return true - }) -} +func (dm *DomainManager) Clear() error { dm.rmMap = &sync.Map{}; dm.matchingFuncCache = util.NewSyncLRUCache(100); return nil } +func (dm *DomainManager) BuildRelationship(n1, n2 string, d ...string) error { return nil } +func (dm *DomainManager) AddLink(n1, n2 string, d ...string) error { return nil } +func (dm *DomainManager) DeleteLink(n1, n2 string, d ...string) error { return nil } +func (dm *DomainManager) HasLink(n1, n2 string, d ...string) (bool, error) { return false, nil } +func (dm *DomainManager) GetRoles(n string, d ...string) ([]string, error) { return nil, nil } +func (dm *DomainManager) GetUsers(n string, d ...string) ([]string, error) { return nil, nil } +func (dm *DomainManager) GetImplicitRoles(n string, d ...string) ([]string, error) { return nil, nil } +func (dm *DomainManager) GetImplicitUsers(n string, d ...string) ([]string, error) { return nil, nil } +func (dm *DomainManager) GetDomains(name string) ([]string, error) { return nil, nil } +func (dm *DomainManager) GetAllDomains() ([]string, error) { return nil, nil } +func (dm *DomainManager) PrintRoles() error { return nil } +func (dm *DomainManager) Match(n1, n2 string) bool { return false } +func (dm *DomainManager) AddMatchingFunc(n string, fn rbac.MatchingFunc) { dm.matchingFunc = fn } +func (dm *DomainManager) AddDomainMatchingFunc(n string, fn rbac.MatchingFunc) { dm.domainMatchingFunc = fn } +func (dm *DomainManager) DeleteDomain(domain string) error { return nil } + + +func (dm *DomainManager) AddLinkConditionFunc(user, role string, fn rbac.LinkConditionFunc) {} +func (dm *DomainManager) SetLinkConditionFuncParams(user, role string, params ...string) {} +func (dm *DomainManager) AddDomainLinkConditionFunc(user, role, domain string, fn rbac.LinkConditionFunc) {} +func (dm *DomainManager) SetDomainLinkConditionFuncParams(user, role, domain string, params ...string) {}