blob: b8165a7fd43725c2ab03b5551a52939c6f7904d3 [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 Util = require("src/util/Util")
local Log = require("src/util/Log")
-- model's struct is map<string, map<string, Assertion>>
local Policy = {}
function Policy:new()
local o = {}
o.logger = Log.getLogger()
setmetatable(o, self)
self.__index = self
return o
end
--[[
* buildRoleLinks initializes the roles in RBAC.
*
* @param rm the role manager.
]]
function Policy:buildRoleLinks(rmMap)
if self.model["g"] then
for ptype, ast in pairs(self.model["g"]) do
local rm = rmMap[ptype]
ast:buildRoleLinks(rm)
end
end
end
--[[
* printPolicy prints the policy to log.
]]
function Policy:printPolicy()
self.logger:info("Policy: \n")
if self.model["p"] then
for k, ast in pairs(self.model["p"]) do
self.logger:info("%s: %s:", k, ast.value)
self.logger:info(ast.policy)
end
end
if self.model["g"] then
for k, ast in pairs(self.model["g"]) do
self.logger:info("%s: %s:", k, ast.value)
self.logger:info(ast.policy)
end
end
end
--[[
* printPolicyMap prints the policyMap to log.
]]
function Policy:printPolicyMap()
self.logger:info("policyMap: \n")
if self.model["p"] then
for k, ast in pairs(self.model["p"]) do
self.logger:info("%s: key,value", k )
for i,v in pairs(ast.policyMap) do
self.logger:info("{%s,%s}", i, v)
end
end
end
if self.model["g"] then
for k, ast in pairs(self.model["g"]) do
self.logger:info("%s: key,value", k )
for i,v in pairs(ast.policyMap) do
self.logger:info("{%s,%s}", i, v)
end
end
end
end
--[[
* savePolicyToText saves the policy to the text.
*
* @return the policy text.
]]
function Policy:savePolicyToText()
local res = ""
if self.model["p"] then
for key, ast in pairs(self.model["p"]) do
for _, rule in pairs(ast.policy) do
local x = string.format("%s, %s\n", key, table.concat(rule, ", "))
res = res .. x
end
end
end
if self.model["g"] then
for key, ast in pairs(self.model["g"]) do
for _, rule in pairs(ast.policy) do
local x = string.format("%s, %s\n", key, table.concat(rule, ", "))
res = res .. x
end
end
end
return res
end
--[[
* clearPolicy clears all current policy.
]]
function Policy:clearPolicy()
if self.model["p"] then
for _, v in pairs(self.model["p"]) do
v.policy = {}
v.policyMap = {}
end
end
if self.model["g"] then
for _, v in pairs(self.model["g"]) do
v.policy = {}
v.policyMap = {}
end
end
end
--[[
* getPolicy gets all rules in a policy.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @return the policy rules of section sec and policy type ptype.
]]
function Policy:getPolicy(sec, ptype)
local policy = Util.deepCopy(self.model[sec][ptype].policy)
return policy
end
--[[
* getFilteredPolicy gets rules based on field filters from a policy.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param fieldIndex the policy rule's start index to be matched.
* @param ... fieldValues the field values to be matched, value ""
* means not to match this field.
* @return the filtered policy rules of section sec and policy type ptype.
]]
function Policy:getFilteredPolicy(sec, ptype, fieldIndex, ...)
local res = {}
local fieldValues = {...}
if type(fieldValues[1]) == "table" then
fieldValues = fieldValues[1]
end
if not self.model[sec] then return res end
if not self.model[sec][ptype] then return res end
for _, rule in pairs(self.model[sec][ptype].policy) do
local matched = true
for i, v in ipairs(fieldValues) do
if v ~= "" and rule[fieldIndex + i] ~= v then
matched = false
break
end
end
if matched then
table.insert(res, rule)
end
end
return res
end
--[[
* hasPolicy determines whether a model has the specified policy rule.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rule the policy rule.
* @return whether the rule exists.
]]
function Policy:hasPolicy(sec, ptype, rule)
if self.model[sec][ptype].policyMap[table.concat(rule,",")] == nil then
return false
else
return true
end
end
--[[
* hasPolicies determines whether a model has any of the specified policies.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rules the policies rules.
* @return whether one is found.
]]
function Policy:hasPolicies(sec, ptype, rules)
for i = 1, #rules do
if self:hasPolicy(sec, ptype, rules[i]) then
return true
end
end
return false
end
--[[
* addPolicy adds a policy rule to the model.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rule the policy rule.
* @return succeeds or not.
]]
function Policy:addPolicy(sec, ptype, rule)
if not self:hasPolicy(sec, ptype, rule) then
table.insert(self.model[sec][ptype].policy, rule)
self.model[sec][ptype].policyMap[table.concat(rule,",")] = #self.model[sec][ptype].policy
if sec == "p" and self.model[sec][ptype].priorityIndex > 0 then
local idxInsert=tonumber(rule[self.model[sec][ptype].priorityIndex])
if rule[self.model[sec][ptype].priorityIndex]~= nil then
local i = #self.model[sec][ptype].policy-1
for j = i, 1, -1 do
local idx=tonumber(self.model[sec][ptype].policy[i+1][self.model[sec][ptype].priorityIndex])
if idx < idxInsert then
self.model[sec][ptype].policy[i+1] = self.model[sec][ptype].policy[i]
self.model[sec][ptype].policyMap[table.concat(self.model[sec][ptype].policy[i+1], ",")] = i+1
else
i = j
break
end
i = j
end
self.model[sec][ptype].policy[i] = rule
self.model[sec][ptype].policyMap[table.concat(rule,",")] = i
end
end
return true
end
return false
end
--[[
* addPolicies adds policy rules to the model.
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rules the policy rules.
* @return succeeds or not.
]]
function Policy:addPolicies(sec, ptype, rules)
return self:addPoliciesWithAffected(sec, ptype, rules) ~= 0
end
--[[
* addPoliciesWithAffected adds policy rules to the model.
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rules the policy rules.
* @return effected.
]]
function Policy:addPoliciesWithAffected(sec, ptype, rules)
local effected = {}
for _, rule in pairs(rules) do
if self:addPolicy(sec, ptype, rule) then
table.insert(effected, rule)
end
end
return effected
end
--[[
* UpdatePolicy updates a policy rule from the model.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param oldRule the old rule.
* @param newRule the new rule.
* @return succeeds or not.
]]
function Policy:updatePolicy(sec, ptype, oldRule, newRule)
if not self:hasPolicy(sec, ptype, oldRule) then return false end
local key = table.concat(oldRule,",")
local index = self.model[sec][ptype].policyMap[key]
self.model[sec][ptype].policy[index] = newRule
self.model[sec][ptype].policyMap[key] = nil
local tempKey = table.concat(newRule,",")
self.model[sec][ptype].policyMap[tempKey] = index
return true
end
-- Updates multiple policy rules from the model.
function Policy:updatePolicies(sec, ptype, oldRules, newRules)
local rollbackFlag = false
local modifiedRuleIndex = {}
local newIndex = 1
for oldIndex, oldRule in pairs(oldRules) do
local oldPolicy = table.concat(oldRule, ",")
if self.model[sec][ptype].policyMap[oldPolicy] == nil then
rollbackFlag = true
break
end
local index = self.model[sec][ptype].policyMap[oldPolicy]
self.model[sec][ptype].policy[index] = newRules[newIndex]
self.model[sec][ptype].policyMap[oldPolicy] = nil
self.model[sec][ptype].policyMap[table.concat(newRules[newIndex], ",")] = index
modifiedRuleIndex[index] = {oldIndex, newIndex}
newIndex = newIndex+1
end
if rollbackFlag then
for index, oldNewIndex in pairs(modifiedRuleIndex) do
self.model[sec][ptype].policy[index] = oldRules[oldNewIndex[1]]
local oldPolicy = table.concat(oldRules[oldNewIndex[1]], ",")
local newPolicy = table.concat(newRules[oldNewIndex[2]], ",")
self.model[sec][ptype].policyMap[newPolicy] = nil
self.model[sec][ptype].policyMap[oldPolicy] = index
end
return false
end
return true
end
function Policy:updateFilteredPolicies(sec, ptype, fieldIndex, fieldValues,newRules)
local tmp = {}
local res = false
local effects = {}
local newRulesIndex=1
if not self.model[sec] then return res end
if not self.model[sec][ptype] then return res end
self.model[sec][ptype].policyMap = {}
for _, rule in pairs(self.model[sec][ptype].policy) do
local matched = true
for i, value in pairs(fieldValues) do
if value ~= "" and rule[fieldIndex+i] ~= value then
matched = false
break
end
end
if matched then
table.insert(effects, rule)
table.insert(tmp, newRules[newRulesIndex])
local tempKey1 = table.concat(newRules[newRulesIndex],",")
self.model[sec][ptype].policyMap[tempKey1] = #tmp
newRulesIndex = newRulesIndex+1
res = true
else
table.insert(tmp, rule)
local tempKey = table.concat(rule,",")
self.model[sec][ptype].policyMap[tempKey] = #tmp
end
end
if res then
self.model[sec][ptype].policy = tmp
end
return res, effects
end
--[[
* removePolicy removes a policy rule from the model.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rule the policy rule.
* @return succeeds or not.
]]
function Policy:removePolicy(sec, ptype, rule)
if self:hasPolicy(sec,ptype,rule) then
local key = table.concat(rule,",")
local index = self.model[sec][ptype].policyMap[key]
table.remove(self.model[sec][ptype].policy, index)
self.model[sec][ptype].policyMap[key] = nil
local length = #self.model[sec][ptype].policy
for i=index, length, 1 do
local tempKey = table.concat(self.model[sec][ptype].policy[i],",")
self.model[sec][ptype].policyMap[tempKey] = i
end
return true
end
return false
end
--[[
* removePolicies removes rules from the current policy.
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rules the policy rules.
* @return succeeds or not.
]]
function Policy:removePolicies(sec, ptype, rules)
return #self:removePoliciesWithEffected(sec, ptype, rules) ~= 0
end
--[[
* removePoliciesWithEffected removes policy rules from the model, and returns effected rules.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param rules the policy rules.
*
* @return effected.
]]
function Policy:removePoliciesWithEffected(sec, ptype, rules)
local effected={}
for _,rule in pairs(rules) do
if self:removePolicy(sec,ptype,rule) then
table.insert(effected,rule)
end
end
return effected
end
--[[
* removeFilteredPolicy removes policy rules based on field filters from the model.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param fieldIndex the policy rule's start index to be matched.
* @param ... fieldValues the field values to be matched, value ""
* means not to match this field.
* @return succeeds or not.
]]
function Policy:removeFilteredPolicy(sec, ptype, fieldIndex, fieldValues)
local tmp = {}
local res = false
local effects = {}
if not self.model[sec] then return res end
if not self.model[sec][ptype] then return res end
self.model[sec][ptype].policyMap = {}
for _, rule in pairs(self.model[sec][ptype].policy) do
local matched = true
for i, value in pairs(fieldValues) do
if value ~= "" and rule[fieldIndex+i] ~= value then
matched = false
break
end
end
if matched then
table.insert(effects, rule)
res = true
else
table.insert(tmp, rule)
local tempKey = table.concat(rule,",")
self.model[sec][ptype].policyMap[tempKey] = #tmp
end
end
if #tmp ~= #self.model[sec][ptype].policy then
self.model[sec][ptype].policy = tmp
end
return res, effects
end
--[[
* getValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed.
*
* @param sec the section, "p" or "g".
* @param ptype the policy type, "p", "p2", .. or "g", "g2", ..
* @param fieldIndex the policy rule's index.
* @return the field values specified by fieldIndex.
]]
function Policy:getValuesForFieldInPolicy(sec, ptype, fieldIndex)
local values = {}
for _, rule in pairs(self.model[sec][ptype].policy) do
values[#values + 1] = rule[fieldIndex]
end
values = Util.arrayRemoveDuplications(values)
return values
end
function Policy:getValuesForFieldInPolicyAllTypes(sec, fieldIndex)
local values = {}
for ptype, _ in pairs(self.model[sec]) do
local tvalues = self:getValuesForFieldInPolicy(sec, ptype, fieldIndex)
for _, v in pairs(tvalues) do
table.insert(values, v)
end
end
return values
end
function Policy:buildIncrementalRoleLinks(rm, op, sec, ptype, rules)
if sec == "g" then
self.model[sec][ptype]:buildIncrementalRoleLinks(rm, op, rules)
end
end
return Policy