Skip to content

Commit

Permalink
feat: Added Built-in Functions and tests for it (#44)
Browse files Browse the repository at this point in the history
* feat: Added Built-in Functions and tests for it

Signed-off-by: Rushikesh Tote <[email protected]>

* fix: globMatch function had different functionality from Casbin Go's globMatch

Signed-off-by: Rushikesh Tote <[email protected]>
  • Loading branch information
rushitote authored May 10, 2021
1 parent db9ef24 commit c2e808a
Show file tree
Hide file tree
Showing 3 changed files with 262 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ jobs:
- name: Set up luarocks
uses: leafo/[email protected]

- name: Install PCRE
run: |
sudo apt-get update
sudo apt-get install libpcre3 libpcre3-dev
- name: Install dependencies
run: |
until luarocks install lualogging
Expand All @@ -54,6 +59,14 @@ jobs:
do
sleep 1
done
until luarocks install lrexlib-pcre
do
sleep 1
done
until luarocks install luaposix
do
sleep 1
done
- name: Run tests
run: busted spec -o htest
Expand Down
145 changes: 145 additions & 0 deletions spec/util/built_in_functions_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
--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 builtInFunctions = require("src.util.BuiltInFunctions")

describe("BuiltInFunctions tests", function ()
it("keyMatch tests", function ()
assert.is.False(BuiltInFunctions.keyMatch("/foo", "/"))
assert.is.True(BuiltInFunctions.keyMatch("/foo", "/foo"))
assert.is.True(BuiltInFunctions.keyMatch("/foo", "/foo*"))
assert.is.False(BuiltInFunctions.keyMatch("/foo", "/foo/*"))
assert.is.False(BuiltInFunctions.keyMatch("/foo/bar", "/foo"))
assert.is.True(BuiltInFunctions.keyMatch("/foo/bar", "/foo*"))
assert.is.True(BuiltInFunctions.keyMatch("/foo/bar", "/foo/*"))
assert.is.False(BuiltInFunctions.keyMatch("/foobar", "/foo"))
assert.is.True(BuiltInFunctions.keyMatch("/foobar", "/foo*"))
assert.is.False(BuiltInFunctions.keyMatch("/foobar", "/foo/*"))
end)

it("keyGet tests", function ()
assert.are.same("", BuiltInFunctions.keyGet("/foo", "/foo"))
assert.are.same("", BuiltInFunctions.keyGet("/foo", "/foo*"))
assert.are.same("", BuiltInFunctions.keyGet("/foo", "/foo/*"))
assert.are.same("", BuiltInFunctions.keyGet("/foo/bar", "/foo"))
assert.are.same("/bar", BuiltInFunctions.keyGet("/foo/bar", "/foo*"))
assert.are.same("bar", BuiltInFunctions.keyGet("/foo/bar", "/foo/*"))
assert.are.same("", BuiltInFunctions.keyGet("/foobar", "/foo"))
assert.are.same("bar", BuiltInFunctions.keyGet("/foobar", "/foo*"))
assert.are.same("", BuiltInFunctions.keyGet("/foobar", "/foo/*"))
end)

it("keyMatch2 tests", function ()
assert.is.False(BuiltInFunctions.keyMatch2("/foo", "/"))
assert.is.True(BuiltInFunctions.keyMatch2("/foo", "/foo"))
assert.is.True(BuiltInFunctions.keyMatch2("/foo", "/foo*"))
assert.is.False(BuiltInFunctions.keyMatch2("/foo", "/foo/*"))
assert.is.False(BuiltInFunctions.keyMatch2("/foo/bar", "/foo")) -- different with KeyMatch.
assert.is.False(BuiltInFunctions.keyMatch2("/foo/bar", "/foo*"))
assert.is.True(BuiltInFunctions.keyMatch2("/foo/bar", "/foo/*"))
assert.is.False(BuiltInFunctions.keyMatch2("/foobar", "/foo")) -- different with KeyMatch.
assert.is.False(BuiltInFunctions.keyMatch2("/foobar", "/foo*"))
assert.is.False(BuiltInFunctions.keyMatch2("/foobar", "/foo/*"))

assert.is.False(BuiltInFunctions.keyMatch2("/", "/:resource"))
assert.is.True(BuiltInFunctions.keyMatch2("/resource1", "/:resource"))
assert.is.False(BuiltInFunctions.keyMatch2("/myid", "/:id/using/:resId"))
assert.is.True(BuiltInFunctions.keyMatch2("/myid/using/myresid", "/:id/using/:resId"))

assert.is.False(BuiltInFunctions.keyMatch2("/proxy/myid", "/proxy/:id/*"))
assert.is.True(BuiltInFunctions.keyMatch2("/proxy/myid/", "/proxy/:id/*"))
assert.is.True(BuiltInFunctions.keyMatch2("/proxy/myid/res", "/proxy/:id/*"))
assert.is.True(BuiltInFunctions.keyMatch2("/proxy/myid/res/res2", "/proxy/:id/*"))
assert.is.True(BuiltInFunctions.keyMatch2("/proxy/myid/res/res2/res3", "/proxy/:id/*"))
assert.is.False(BuiltInFunctions.keyMatch2("/proxy/", "/proxy/:id/*"))

assert.is.True(BuiltInFunctions.keyMatch2("/alice", "/:id"))
assert.is.True(BuiltInFunctions.keyMatch2("/alice/all", "/:id/all"))
assert.is.False(BuiltInFunctions.keyMatch2("/alice", "/:id/all"))
assert.is.False(BuiltInFunctions.keyMatch2("/alice/all", "/:id"))

assert.is.False(BuiltInFunctions.keyMatch2("/alice/all", "/:/all"))
end)

it("keyMatch3 tests", function ()
assert.is.True(BuiltInFunctions.keyMatch3("/foo", "/foo"))
assert.is.True(BuiltInFunctions.keyMatch3("/foo", "/foo*"))
assert.is.False(BuiltInFunctions.keyMatch3("/foo", "/foo/*"))
assert.is.False(BuiltInFunctions.keyMatch3("/foo/bar", "/foo"))
assert.is.False(BuiltInFunctions.keyMatch3("/foo/bar", "/foo*"))
assert.is.True(BuiltInFunctions.keyMatch3("/foo/bar", "/foo/*"))
assert.is.False(BuiltInFunctions.keyMatch3("/foobar", "/foo"))
assert.is.False(BuiltInFunctions.keyMatch3("/foobar", "/foo*"))
assert.is.False(BuiltInFunctions.keyMatch3("/foobar", "/foo/*"))

assert.is.False(BuiltInFunctions.keyMatch3("/", "/{resource}"))
assert.is.True(BuiltInFunctions.keyMatch3("/resource1", "/{resource}"))
assert.is.False(BuiltInFunctions.keyMatch3("/myid", "/{id}/using/{resId}"))
assert.is.True(BuiltInFunctions.keyMatch3("/myid/using/myresid", "/{id}/using/{resId}"))

assert.is.False(BuiltInFunctions.keyMatch3("/proxy/myid", "/proxy/{id}/*"))
assert.is.True(BuiltInFunctions.keyMatch3("/proxy/myid/", "/proxy/{id}/*"))
assert.is.True(BuiltInFunctions.keyMatch3("/proxy/myid/res", "/proxy/{id}/*"))
assert.is.True(BuiltInFunctions.keyMatch3("/proxy/myid/res/res2", "/proxy/{id}/*"))
assert.is.True(BuiltInFunctions.keyMatch3("/proxy/myid/res/res2/res3", "/proxy/{id}/*"))
assert.is.False(BuiltInFunctions.keyMatch3("/proxy/", "/proxy/{id}/*"))

assert.is.False(BuiltInFunctions.keyMatch3("/myid/using/myresid", "/{id/using/{resId}"))
end)

it("regexMatch tests", function ()
assert.is.True(BuiltInFunctions.regexMatch("/topic/create", "/topic/create"))
assert.is.True(BuiltInFunctions.regexMatch("/topic/create/123", "/topic/create"))
assert.is.False(BuiltInFunctions.regexMatch("/topic/delete", "/topic/create"))
assert.is.False(BuiltInFunctions.regexMatch("/topic/edit", "/topic/edit/[0-9]+"))
assert.is.True(BuiltInFunctions.regexMatch("/topic/edit/123", "/topic/edit/[0-9]+"))
assert.is.False(BuiltInFunctions.regexMatch("/topic/edit/abc", "/topic/edit/[0-9]+"))
assert.is.False(BuiltInFunctions.regexMatch("/foo/delete/123", "/topic/delete/[0-9]+"))
assert.is.True(BuiltInFunctions.regexMatch("/topic/delete/0", "/topic/delete/[0-9]+"))
assert.is.False(BuiltInFunctions.regexMatch("/topic/edit/123s", "/topic/delete/[0-9]+"))
end)

it("globMatch tests", function ()
assert.is.True(BuiltInFunctions.globMatch("/foo", "/foo"))
assert.is.True(BuiltInFunctions.globMatch("/foo", "/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/foo", "/foo/*"))
assert.is.False(BuiltInFunctions.globMatch("/foo/bar", "/foo"))
assert.is.False(BuiltInFunctions.globMatch("/foo/bar", "/foo*"))
assert.is.True(BuiltInFunctions.globMatch("/foo/bar", "/foo/*"))
assert.is.False(BuiltInFunctions.globMatch("/foobar", "/foo"))
assert.is.True(BuiltInFunctions.globMatch("/foobar", "/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/foobar", "/foo/*"))

assert.is.False(BuiltInFunctions.globMatch("/prefix/foo", "*/foo"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foo", "*/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foo", "*/foo/*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foo/bar", "*/foo"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foo/bar", "*/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foo/bar", "*/foo/*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foobar", "*/foo"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foobar", "*/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/foobar", "*/foo/*"))

assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foo", "*/foo"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foo", "*/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foo", "*/foo/*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foo/bar", "*/foo"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foo/bar", "*/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foo/bar", "*/foo/*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foobar", "*/foo"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foobar", "*/foo*"))
assert.is.False(BuiltInFunctions.globMatch("/prefix/subprefix/foobar", "*/foo/*"))

end)
end)
104 changes: 104 additions & 0 deletions src/util/BuiltInFunctions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
--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 rex = require ("rex_pcre")
local posix = require("posix.fnmatch")

BuiltInFunctions = {}

-- KeyMatch determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
-- For example, "/foo/bar" matches "/foo/*"
function BuiltInFunctions.keyMatch(key1, key2)
local i, _ = string.find(key2, "*")

if not i then
return (key1 == key2)
end

if #key1>=i then
return (string.sub(key1, 1, i-1) == string.sub(key2, 1, i-1))
end
return (key1 == string.sub(key2, 1, i-1))
end

-- KeyGet returns the matched part
-- For example, "/foo/bar/foo" matches "/foo/*"
-- "bar/foo" will been returned
function BuiltInFunctions.keyGet(key1, key2)
local i, _ = string.find(key2, "*")

if not i then
return ""
end
if #key1>=i then
if string.sub(key1, 1, i-1) == string.sub(key2, 1, i-1) then
return string.sub(key1, i)
end
end
return ""
end

-- KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
-- For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource"
function BuiltInFunctions.keyMatch2(key1, key2)
key2 = string.gsub(key2, "/%*", "/.*")
local key = rex.gsub(key2, ":[^/]+", "[^/]+")
return BuiltInFunctions.regexMatch(key1, "^"..key.."$")
end

-- KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
-- For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}"
function BuiltInFunctions.keyMatch3(key1, key2)
key2 = string.gsub(key2, "/%*", "/.*")
local key = rex.gsub(key2, "{[^/]+}", "[^/]+")
return BuiltInFunctions.regexMatch(key1, "^"..key.."$")
end

-- RegexMatch determines whether key1 matches the pattern of key2 in regular expression.
function BuiltInFunctions.regexMatch(key1, key2)
local res = rex.match(key1, key2)
if res then
return true
else
return false
end
end

-- GlobMatch determines whether key1 matches the pattern of key2 using glob pattern
function BuiltInFunctions.globMatch(key1, key2)
if posix.fnmatch(key2, key1, posix.FNM_PATHNAME or posix.FNM_PERIOD) == 0 then
return true
else
return false
end
end

-- GenerateGFunction is the factory method of the g(_, _) function.
function BuiltInFunctions.generateGFunction(rm)
local function f(args)
local name1 = args[1]
local name2 = args[2]

if not rm then
return name1 == name2
elseif #args==2 then
return rm:hasLink(name1, name2)
else
local domain = args[3]
return rm:hasLink(name1, name2, domain)
end
end

return f
end

0 comments on commit c2e808a

Please sign in to comment.