blob: 0fd288ad9279b720f02f224d2585ae74d6b9a95d [file]
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you 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
--
-- https://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 CoreEnforcer = require("src/main/CoreEnforcer")
--[[
* tryCall calls fn, usually an adapter operation, and normalizes the way a
* failure is reported: an adapter may either raise a Lua error or return
* "false, err", and both must reach the caller instead of being dropped.
*
* @param fn the function to call.
* @return true on success, or false plus the error message on failure.
* Operations that the adapter does not implement count as success,
* because persistence of them is optional.
]]
local function tryCall(fn)
local status, ok, err = pcall(fn)
if status == false then
local message = tostring(ok)
if string.sub(message, -15) == "not implemented" then
-- log, continue
return true
end
return false, message
end
if ok == false or (ok == nil and err ~= nil) then
return false, tostring(err or "unknown error")
end
return true
end
-- InternalEnforcer = CoreEnforcer + Internal API.
local InternalEnforcer = {}
setmetatable(InternalEnforcer, CoreEnforcer)
InternalEnforcer.__index = InternalEnforcer
function InternalEnforcer:shouldPersist()
return self.adapter and self.autoSave
end
--[[
* addPolicy adds a rule to the current policy.
]]
function InternalEnforcer:addPolicy(sec, ptype, rule)
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:addPolicies(sec, ptype,{rule})
return true
end
if self.model:hasPolicy(sec, ptype, rule) then
return false
end
if self:shouldPersist() then
local ok, err = tryCall(function () return self.adapter:addPolicy(sec, ptype, rule) end)
if not ok then
return false, err
end
end
self.model:addPolicy(sec, ptype, rule)
if sec == "g" then
local rules = {}
table.insert(rules, rule)
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_ADD, ptype, rules)
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForAddPolicy then
self.watcher:updateForAddPolicy(sec, ptype, rule)
else
self.watcher:update()
end
end
return true
end
--[[
* addPolicies adds rules to the current policy.
]]
function InternalEnforcer:addPolicies(sec, ptype, rules)
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:addPolicies(sec, ptype,rules)
return true
end
if self.model:hasPolicies(sec, ptype, rules) then
return false
end
if self:shouldPersist() then
local ok, err = tryCall(function ()
if self.adapter.addPolicies then
return self.adapter:addPolicies(sec, ptype, rules)
end
end)
if not ok then
return false, err
end
end
self.model:addPolicies(sec, ptype, rules)
if sec == "g" then
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_ADD, ptype, rules)
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForAddPolicies then
self.watcher:updateForAddPolicies(sec, ptype, rules)
else
self.watcher:update()
end
end
return true
end
--[[
* buildIncrementalRoleLinks provides incremental build the role inheritance relations.
* @param op Policy operations.
* @param ptype policy type.
* @param rules the rules.
]]
function InternalEnforcer:buildIncrementalRoleLinks(op, ptype, rules)
self.model:buildIncrementalRoleLinks(self.rmMap[ptype], op, "g", ptype, rules)
end
--[[
* removePolicy removes a rule from the current policy.
]]
function InternalEnforcer:removePolicy(sec, ptype, rule)
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:removePolicies(sec, ptype,{rule})
return true
end
if self:shouldPersist() then
local ok, err = tryCall(function () return self.adapter:removePolicy(sec, ptype, rule) end)
if not ok then
return false, err
end
end
local ruleRemoved = self.model:removePolicy(sec, ptype, rule)
if not ruleRemoved then
return false
end
if sec == "g" then
local rules = {}
table.insert(rules, rule)
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_REMOVE, ptype, rules)
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForRemovePolicy then
self.watcher:updateForRemovePolicy(sec, ptype, rule)
else
self.watcher:update()
end
end
return true
end
--[[
* updatePolicy updates an authorization rule from the current policy.
* @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 InternalEnforcer:updatePolicy(sec, ptype, oldRule, newRule)
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:updatePolicy(sec, ptype,oldRule, newRule)
return true
end
if self:shouldPersist() then
local ok, err = tryCall(function () return self.adapter:updatePolicy(sec, ptype, oldRule, newRule) end)
if not ok then
return false, err
end
end
local ruleUpdated = self.model:updatePolicy(sec, ptype, oldRule, newRule)
if not ruleUpdated then
return false
end
if sec == "g" then
local ok, err = tryCall(function ()
local oldRules = {}
table.insert(oldRules, oldRule)
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_REMOVE, ptype, oldRules)
end)
if not ok then
return false, err
end
ok, err = tryCall(function ()
local newRules = {}
table.insert(newRules, newRule)
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_ADD, ptype, newRules)
end)
if not ok then
return false, err
end
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForUpdatePolicy then
self.watcher:updateForUpdatePolicy(sec, ptype, oldRule, newRule)
else
self.watcher:update()
end
end
return true
end
function InternalEnforcer:updatePolicies(sec, ptype, oldRules, newRules)
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:updatePolicies(sec, ptype,oldRules, newRules)
return true
end
if self:shouldPersist() then
local ok, err = tryCall(function () return self.adapter:updatePolicies(sec, ptype, oldRules, newRules) end)
if not ok then
return false, err
end
end
local ruleUpdated = self.model:updatePolicies(sec, ptype, oldRules, newRules)
if not ruleUpdated then
return false
end
if sec == "g" then
local ok, err = tryCall(function ()
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_REMOVE, ptype, oldRules)
end)
if not ok then
return false, err
end
ok, err = tryCall(function ()
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_ADD, ptype, newRules)
end)
if not ok then
return false, err
end
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForUpdatePolicies then
self.watcher:updateForUpdatePolicies(sec, ptype, oldRules, newRules)
else
self.watcher:update()
end
end
return true
end
--[[
* removePolicies removes rules from the current policy.
]]
function InternalEnforcer:removePolicies(sec, ptype, rules)
if not self.model:hasPolicies(sec, ptype, rules) then
return false
end
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:removePolicies(sec, ptype,rules)
return true
end
if self:shouldPersist() then
local ok, err = tryCall(function ()
if self.adapter.removePolicies then
return self.adapter:removePolicies(sec, ptype, rules)
end
end)
if not ok then
return false, err
end
end
local rulesRemoved = self.model:removePolicies(sec, ptype, rules)
if not rulesRemoved then
return false
end
if sec == "g" then
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_REMOVE, ptype, rules)
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForRemovePolicies then
self.watcher:updateForRemovePolicies(sec, ptype, rules)
else
self.watcher:update()
end
end
return true
end
--[[
* removeFilteredPolicy removes rules based on field filters from the current policy.
]]
function InternalEnforcer:removeFilteredPolicy(sec, ptype, fieldIndex, fieldValues)
if fieldValues == nil or #fieldValues == 0 then
return false
end
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:removeFilteredPolicy(sec, ptype, fieldIndex, fieldValues)
return true
end
if self:shouldPersist() then
local ok, err = tryCall(function () return self.adapter:removeFilteredPolicy(sec, ptype, fieldIndex, fieldValues) end)
if not ok then
return false, err
end
end
local isRuleRemoved, effects = self.model:removeFilteredPolicy(sec, ptype, fieldIndex, fieldValues)
if not isRuleRemoved then
return false
end
if sec == "g" then
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_REMOVE, ptype, effects)
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForRemoveFilteredPolicy then
self.watcher:updateForRemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues)
else
self.watcher:update()
end
end
return true
end
function InternalEnforcer:updateFilteredPolicies(sec, ptype, newRules, fieldIndex, fieldValues)
if fieldValues == nil or #fieldValues == 0 then
return false
end
local oldRules = self.model:getFilteredPolicy(sec, ptype, fieldIndex, fieldValues)
if self:shouldPersist() then
local ok, err = tryCall(function () return self.adapter:updateFilteredPolicies(sec, ptype, newRules, fieldIndex, fieldValues) end)
if not ok then
return false, err
end
end
if self.dispatcher~=nil and self.autoNotifyDispatcher then
self.dispatcher:updateFilteredPolicies(sec, ptype, oldRules, newRules)
return true
end
local ruleChanged = self.model:removePolicies(sec, ptype, oldRules)
self.model:addPolicies(sec, ptype, newRules)
ruleChanged = ruleChanged and #newRules ~= 0
if ruleChanged==false then
return ruleChanged
end
if sec == "g" then
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_REMOVE, ptype, oldRules)
self:buildIncrementalRoleLinks(self.model.PolicyOperations.POLICY_ADD, ptype, newRules)
end
if self.watcher and self.autoNotifyWatcher then
if self.watcher.updateForUpdatePolicies then
self.watcher:updateForUpdatePolicies(oldRules, newRules)
else
self.watcher:update()
end
end
return true
end
function InternalEnforcer:getDomainIndex(ptype)
if not self.model.model["p"] then return end
if not self.model.model["p"][ptype] then return end
local p = self.model.model["p"][ptype]
local pattern = ptype .. "_dom"
local index = #p.tokens + 1
for i, token in pairs(p.tokens) do
if token == pattern then
index = i
break
end
end
return index
end
return InternalEnforcer