blob: bb524f1f601316b7d0ed2a43b9a6d332f0aece4e [file] [log] [blame]
--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