Skip to content

Commit

Permalink
feat: Implemented CoreEnforcer with basic tests (#46)
Browse files Browse the repository at this point in the history
Signed-off-by: Rushikesh Tote <[email protected]>
  • Loading branch information
rushitote authored May 12, 2021
1 parent c2e808a commit cf39efb
Show file tree
Hide file tree
Showing 10 changed files with 1,850 additions and 108 deletions.
1,518 changes: 1,518 additions & 0 deletions modules/luaxp.lua

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions spec/main/enforcer_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--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 enforcer_module = require("src.main.Enforcer")
local path = os.getenv("PWD") or io.popen("cd"):read()

describe("Enforcer tests", function ()
it("basic test", function ()
local model = path .. "/examples/basic_model.conf"
local policy = path .. "/examples/basic_policy.csv"

local e = Enforcer:new(model, policy)
assert.is.True(e:enforce("alice", "data1", "read"))
assert.is.False(e:enforce("alice", "data2", "read"))
end)

it("rbac test", function ()
local model = path .. "/examples/rbac_model.conf"
local policy = path .. "/examples/rbac_policy.csv"

local e = Enforcer:new(model, policy)
assert.is.True(e:enforce("alice", "data1", "read"))
assert.is.True(e:enforce("alice", "data2", "read"))
assert.is.True(e:enforce("alice", "data2", "write"))
assert.is.False(e:enforce("bob", "data1", "read"))
assert.is.True(e:enforce("bob", "data2", "write"))
assert.is.False(e:enforce("bogus", "data2", "write")) -- Non-existent subject
end)

it("keyMatch test", function ()
local model = path .. "/examples/keymatch_model.conf"
local policy = path .. "/examples/keymatch_policy.csv"

local e = Enforcer:new(model, policy)
assert.is.True(e:enforce("cathy", "/cathy_data", "GET"))
end)
end)
236 changes: 174 additions & 62 deletions src/main/CoreEnforcer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,105 @@
--See the License for the specific language governing permissions and
--limitations under the License.

require("src.effect.DefaultEffector")
require("src.effect.Effector")
require("src.model.FunctionMap")
require("src.model.Model")
require("src.persist.file_adapter.FileAdapter")
require("src.rbac.DefaultRoleManager")
require("src.util.BuiltInFunctions")

local luaxp = require("modules.luaxp")

CoreEnforcer = {
model_path,
model,
fm,
adapter,
watcher,
rm,
dispatcher,
autoSave,
autoBuildRoleLinks,
enabled = false,
autoSave = false,
autoBuildRoleLinks = false,
autoNotifyWatcher = true,
autoNotifyDispatcher = true,
aviatorEval, -- cached instance of AviatorEvaluatorInstance
modelModCount, -- detect changes in Model so that we can invalidate AviatorEvaluatorInstance cache
}
CoreEnforcer.__index = CoreEnforcer

--[[
private:
Effector eft
boolean enabled
]]
function CoreEnforcer:new(model, adapter)
local o = {}
setmetatable(o, self)
self.__index = self
self.logger = Log:getLogger()

if type(model) == "string" then
if type(adapter) == "string" then
o:initWithFile(model, adapter)
else
o:initWithAdapter(model, adapter)
end
else
if type(adapter) == "string" then
error("Invalid parameters for Enforcer.")
else
o:initWithModelAndAdapter(model, adapter)
end
end
return o
end

local function initialize()
function CoreEnforcer:initWithFile(modelPath, policyPath)
local a = FileAdapter:new(policyPath)
self:initWithAdapter(modelPath, a)
end

function CoreEnforcer:initWithAdapter(modelPath, adapter)
local m = self:newModel(modelPath)
self:initWithModelAndAdapter(m, adapter)
self.modelPath = modelPath
end

--[[
* newModel creates a model.
*
* @param text the model text.
* @return the model.
]]
function CoreEnforcer:newModel(text)
m = Model:Model()
if text~=nil then
m.loadModelFromText(text);
function CoreEnforcer:initWithModelAndAdapter(m, adapter)
if not Util.isInstance(m, Model) or not Util.isInstance(adapter, Adapter) then
error("Invalid parameters for Enforcer.")
end

self.adapter = adapter
self.model = m
self.model:printModel()

self:initialize()
if self.adapter and not self:isFiltered() then
self:loadPolicy()
end
end

function CoreEnforcer:initialize()
self.rmMap = {}
self.enabled = true
self.autoSave = true
self.autoBuildRoleLinks = true

self:initBuildRoleLinks()
end

--
function CoreEnforcer:initBuildRoleLinks()
if self.model.model["g"] then
for ptype, _ in pairs(self.model.model["g"]) do
self.rmMap[ptype] = DefaultRoleManager:new(10)
end
end
return m
end

--[[
* newModelFromPath creates a model.
* newModel creates a model.
*
* @param modelPath the path of the model file.
* @param unused unused parameter, just for differentiating with
* newModel(String text).
* @return the model.
]]
function CoreEnforcer:newModelFromPath(modelPath, unused)
m = Model:Model()
if modelPath~='' then
m.loadModel(text);
function CoreEnforcer:newModel(modelPath, text)
local m = Model:new()
if modelPath ~= "" then
m:loadModel(modelPath)
else
m:loadModelFromText(text)
end
return m
end
Expand All @@ -75,11 +121,7 @@ end
* and needs to be reloaded by calling LoadPolicy().
]]
function CoreEnforcer:loadModel()
model = self:newModel()
model:loadModel(self.modelPath)
model:printModel()
self.fm = FunctionMap:loadFunctionMap();
self.aviatorEval = nil

end

--[[
Expand All @@ -98,8 +140,6 @@ end
]]
function CoreEnforcer:setModel(model)
self.model = model
self.fm = FunctionMap:loadFunctionMap();
self.aviatorEval = nil
end

--[[
Expand Down Expand Up @@ -127,7 +167,6 @@ end
]]
function CoreEnforcer:setWatcher(watcher)
self.watcher = watcher
watcher:setUpdateCallback(loadPolicy())
end

--[[
Expand Down Expand Up @@ -169,10 +208,10 @@ end
]]
function CoreEnforcer:loadPolicy()
self.model:clearPolicy()
self.adapter:loadPolicy(model);
self.adapter:loadPolicy(self.model);
self.model:printPolicy()
if autoBuildRoleLinks then
buildRoleLinks()
if self.autoBuildRoleLinks then
self:buildRoleLinks()
end
end

Expand All @@ -191,7 +230,7 @@ end
* @return if the loaded policy has been filtered.
]]
function CoreEnforcer:isFiltered()

return self.adapter.isFiltered
end

--[[
Expand Down Expand Up @@ -246,8 +285,11 @@ end
* role inheritance relations.
]]
function CoreEnforcer:buildRoleLinks()
self.rm.clear()
self.model.buildRoleLinks(rm)
for _, rm in pairs(self.rmMap) do
rm:clear()
end

self.model:buildRoleLinks(self.rmMap)
end

--[[
Expand All @@ -259,28 +301,98 @@ end
* @return whether to allow the request.
]]
function CoreEnforcer:enforce(...)
local rvals = {...}

end


function CoreEnforcer:getRTokens(parameters, ...)
if not self.enabled then
return false
end

local functions = FunctionMap:new()

if self.model.model["g"] then
for key, ast in pairs(self.model.model["g"]) do
local rm = ast.RM
functions[key] = BuiltInFunctions.generateGFunction(rm)
end
end

end
if not self.model.model["m"] then
error("model is undefined")
end

function CoreEnforcer:validateEnforce(...)
if not self.model.model["m"]["m"] then
error("model is undefined")
end

end
local rTokens = self.model.model["r"]["r"].tokens
local pTokens = self.model.model["p"]["p"].tokens

function CoreEnforcer:validateEnforceSection(section, ...)
if #rTokens ~= #rvals then
error("invalid request size")
end

end
local expString = self.model.model["m"]["m"].value
-- TODO: hasEval
local policyLen = #self.model.model["p"]["p"].policy

local policyEffects = {}

if policyLen ~=0 then
for i, pvals in pairs(self.model.model["p"]["p"].policy) do
if #pTokens ~= #pvals then
error("invalid policy size")
end

local context = {}
for k, v in pairs(functions) do
context[k] = v
end
for k, v in pairs(rTokens) do
context[v] = rvals[k]
end
for k, v in pairs(pTokens) do
context[v] = pvals[k]
end

local res, err = luaxp.evaluate(expString, context)
if err then
error("evaluation error: " .. err.message)
end

local c = true
if type(res) == "boolean" then
if not res then
table.insert(policyEffects, Effect.INDETERMINATE)
c = false
end
elseif type(res) == "number" then
if res == 0 then
table.insert(policyEffects, Effect.INDETERMINATE)
c = false
end
else
error("matcher result should be boolean or integer")
end

if context["p_eft"] and c then
local eft = context["p_eft"]
if eft == "allow" then
table.insert(policyEffects, Effect.ALLOW)
elseif eft == "deny" then
table.insert(policyEffects, Effect.DENY)
else
table.insert(policyEffects, Effect.INDETERMINATE)
end
elseif c then
table.insert(policyEffects, Effect.ALLOW)
end
end
else

--[[
* Invalidate cache of compiled model matcher expression. This is done automatically most of the time, but you may
* need to call it explicitly if you manipulate directly Model.
]]
function CoreEnforcer:resetExpressionEvaluator()
self.aviatorEval = null
end

local finalResult = DefaultEffector:mergeEffects(self.model.model["e"]["e"].value, policyEffects)
return finalResult
end

function CoreEnforcer:isAutoNotifyWatcher()
Expand Down
6 changes: 3 additions & 3 deletions src/main/Enforcer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
--See the License for the specific language governing permissions and
--limitations under the License.

Enforcer = {}


require("src.main.CoreEnforcer")

Enforcer = {}
setmetatable(Enforcer, CoreEnforcer)

return Enforcer
Loading

0 comments on commit cf39efb

Please sign in to comment.