| --Copyright 2021 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. |
| |
| local Role = require("src/rbac/Role") |
| local Log = require("src/util/Log") |
| |
| local DefaultRoleManager = { |
| maxHierarchyLevel = 0 |
| } |
| |
| --[[ |
| * In order to use a specific role name matching function, set explicitly the role manager on |
| * the Enforcer and rebuild role links (you can optimize by using minimal enforcer constructor). |
| * |
| * <pre> |
| * final Enforcer e = new Enforcer("model.conf"); |
| * e.setAdapter(new FileAdapter("policies.csv")); |
| * e.setRoleManager(new DefaultRoleManager(10, BuiltInFunctions::domainMatch)); |
| * e.loadPolicy(); |
| * </pre> |
| * |
| * |
| * @param maxHierarchyLevel the maximized allowed RBAC hierarchy level. |
| * @param matchingFunc a matcher for supporting pattern in g |
| * @param domainMatchingFunc a matcher for supporting domain pattern in g |
| ]] |
| function DefaultRoleManager:new(maxHierarchyLevel, matchingFunc, domainMatchingFunc) |
| local o = {} |
| setmetatable(o, self) |
| self.__index = self |
| o.logger = Log.getLogger() |
| o.allRoles = {} |
| o.maxHierarchyLevel = maxHierarchyLevel |
| o.matchingFunc = matchingFunc |
| o.domainMatchingFunc = domainMatchingFunc |
| return o |
| end |
| |
| --[[ |
| * addMatchingFunc adds a Matching Function to the RM |
| * if nil is passed in, it removes the function |
| ]] |
| function DefaultRoleManager:addMatchingFunc(matchingFunc) |
| self.matchingFunc = matchingFunc |
| end |
| |
| --[[ |
| * addDomainMatchingFunc adds a Domain Matching Function to the RM |
| * if nil is passed in, it removes the function |
| ]] |
| function DefaultRoleManager:addDomainMatchingFunc(domainMatchingFunc) |
| self.domainMatchingFunc = domainMatchingFunc |
| end |
| |
| function DefaultRoleManager:hasRole(role) |
| |
| if not self.matchingFunc and not self.domainMatchingFunc then |
| return self.allRoles[role:getKey()] |
| end |
| |
| for _, r in pairs(self.allRoles) do |
| local flag = true |
| if self.matchingFunc then |
| if not self.matchingFunc(role.name, r.name) then |
| flag = false |
| end |
| else |
| if role.name ~= r.name then |
| flag = false |
| end |
| end |
| |
| if self.domainMatchingFunc then |
| if not self.domainMatchingFunc(role.domain, r.domain) then |
| flag = false |
| end |
| else |
| if role.domain ~= r.domain then |
| flag = false |
| end |
| end |
| |
| if flag then |
| return true |
| end |
| end |
| end |
| |
| function DefaultRoleManager:createRole(name, domain) |
| local role = Role:new(name, domain) |
| local key |
| if domain and domain ~= "" then |
| key = domain .. "::" .. name |
| else |
| key = name |
| end |
| |
| if not self.allRoles[key] then |
| self.allRoles[key] = role |
| end |
| |
| return self.allRoles[key] |
| end |
| |
| -- * clear clears all stored data and resets the role manager to the initial state. |
| function DefaultRoleManager:clear() |
| self.allRoles = {} |
| end |
| |
| --[[ |
| * addLink adds the inheritance link between role: name1 and role: name2. aka role: name1 |
| * inherits role: name2. domain is a prefix to the roles. |
| ]] |
| function DefaultRoleManager:addLink(name1, name2, ...) |
| local domain = {...} |
| if #domain > 1 then |
| error("domain should be only 1 parameter") |
| elseif #domain == 1 then |
| domain = domain[1] |
| else |
| domain = "" |
| end |
| |
| local role1 = self:createRole(name1, domain) |
| local role2 = self:createRole(name2, domain) |
| role1:addRole(role2) |
| |
| if self.matchingFunc then |
| for _, role in pairs(self.allRoles) do |
| local flag = true |
| if self.domainMatchingFunc then |
| if not self.domainMatchingFunc(domain, role.domain) then |
| flag = false |
| end |
| else |
| if domain ~= role.domain then |
| flag = false |
| end |
| end |
| |
| if flag then |
| if self.matchingFunc(role.name, role1.name) and role1.name ~= role.name then |
| self.allRoles[role:getKey()]:addRole(role1) |
| end |
| if self.matchingFunc(role.name, role2.name) and role2.name ~= role.name then |
| self.allRoles[role2:getKey()]:addRole(role) |
| end |
| if self.matchingFunc(role1.name, role.name) and role1.name ~= role.name then |
| self.allRoles[role:getKey()]:addRole(role1) |
| end |
| if self.matchingFunc(role2.name, role.name) and role2.name ~= role.name then |
| self.allRoles[role2:getKey()]:addRole(role) |
| end |
| end |
| end |
| end |
| end |
| |
| --[[ |
| * 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. |
| ]] |
| function DefaultRoleManager:deleteLink(name1, name2, ...) |
| local domain = {...} |
| if #domain > 1 then |
| error("domain should be only 1 parameter") |
| elseif #domain == 1 then |
| domain = domain[1] |
| else |
| domain = "" |
| end |
| |
| local role1, role2 = self:twoRoleDomainWrapper(name1, name2, domain) |
| |
| if not (self:hasRole(role1)) or not (self:hasRole(role2)) then |
| error("name1 or name2 does not exist") |
| end |
| |
| role1:deleteRole(role2) |
| end |
| |
| -- hasLink determines whether role: name1 inherits role: name2. domain is a prefix to the roles. |
| function DefaultRoleManager:hasLink(name1, name2, ...) |
| local domain = {...} |
| if #domain > 1 then |
| error("domain should be only 1 parameter") |
| elseif #domain == 1 then |
| domain = domain[1] |
| else |
| domain = "" |
| end |
| |
| local role1, role2 = self:twoRoleDomainWrapper(name1, name2, domain) |
| |
| if role1.name == role2.name then |
| return true |
| end |
| |
| if not (self:hasRole(role1)) or not (self:hasRole(role2)) then |
| return false |
| end |
| |
| if not self.matchingFunc and not self.domainMatchingFunc then |
| return role1:hasRole(role2, self.maxHierarchyLevel) |
| end |
| |
| for _, role in pairs(self.allRoles) do |
| local flag = true |
| if self.domainMatchingFunc then |
| if not self.domainMatchingFunc(domain, role.domain) then |
| flag = false |
| end |
| else |
| if domain ~= role.domain then |
| flag = false |
| end |
| end |
| |
| if flag then |
| if self.matchingFunc then |
| if self.matchingFunc(role1.name, role.name) and role:hasRole(role2, self.maxHierarchyLevel, self.matchingFunc, self.domainMatchingFunc) then |
| return true |
| end |
| else |
| if role1.name == role.name and role:hasRole(role2, self.maxHierarchyLevel, self.matchingFunc, self.domainMatchingFunc) then |
| return true |
| end |
| end |
| end |
| end |
| |
| return false |
| end |
| |
| -- * getRoles gets the roles that a subject inherits. domain is a prefix to the roles. |
| function DefaultRoleManager:getRoles(name, ...) |
| local domain = {...} |
| if #domain > 1 then |
| error("domain should be only 1 parameter") |
| elseif #domain == 1 then |
| domain = domain[1] |
| else |
| domain = "" |
| end |
| |
| local role = self:roleDomainWrapper(name, domain) |
| |
| if not self:hasRole(role) then |
| return {} |
| end |
| |
| local roles = self:createRole(name, domain):getRoles() |
| |
| return roles |
| end |
| |
| -- getUsers gets the users that inherits a subject. |
| function DefaultRoleManager:getUsers(name, ...) |
| local domain = {...} |
| if #domain > 1 then |
| error("domain should be only 1 parameter") |
| elseif #domain == 1 then |
| domain = domain[1] |
| else |
| domain = "" |
| end |
| |
| local targetRole = self:roleDomainWrapper(name, domain) |
| |
| local names = {} |
| for _, role in pairs(self.allRoles) do |
| if role:hasDirectRole(targetRole) then |
| table.insert(names, role.name) |
| end |
| end |
| |
| return names |
| end |
| |
| -- printRoles prints all the roles to log. |
| function DefaultRoleManager:printRoles() |
| |
| self.logger:info("Roles: ") |
| for _, role in pairs(self.allRoles) do |
| local text = role:toString() |
| if text then |
| self.logger:info(text) |
| end |
| end |
| |
| end |
| |
| function DefaultRoleManager:roleDomainWrapper(name, domain) |
| if type(domain) ~= "string" then |
| if not domain or #domain==0 then |
| domain = "" |
| elseif #domain == 1 then |
| domain = domain[1] |
| else |
| error("domain should be only 1 parameter") |
| end |
| end |
| |
| local role = Role:new(name, domain) |
| if not self:hasRole(role) then |
| return role |
| end |
| return self:createRole(name, domain) |
| end |
| |
| function DefaultRoleManager:twoRoleDomainWrapper(name1, name2, domain) |
| return self:roleDomainWrapper(name1, domain), self:roleDomainWrapper(name2, domain) |
| end |
| |
| return DefaultRoleManager |