Skip to content

Commit

Permalink
feat: key_match4 builtin operator added (#183)
Browse files Browse the repository at this point in the history
Signed-off-by: ffyuanda <[email protected]>
  • Loading branch information
ffyuanda authored Aug 27, 2021
1 parent c502fea commit ef127b3
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
50 changes: 50 additions & 0 deletions casbin/util/builtin_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

KEY_MATCH2_PATTERN = re.compile(r"(.*?):[^\/]+(.*?)")
KEY_MATCH3_PATTERN = re.compile(r"(.*?){[^\/]+}(.*?)")
KEY_MATCH4_PATTERN = re.compile(r"{([^/]+)}")


def key_match(key1, key2):
Expand Down Expand Up @@ -68,6 +69,55 @@ def key_match3_func(*args):
return key_match3(name1, name2)


def key_match4(key1: str, key2: str) -> bool:
"""
key_match4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
Besides what key_match3 does, key_match4 can also match repeated patterns:
"/parent/123/child/123" matches "/parent/{id}/child/{id}"
"/parent/123/child/456" does not match "/parent/{id}/child/{id}"
But key_match3 will match both.
"""
key2 = key2.replace("/*", "/.*")

tokens: [str] = []

def repl(matchobj):
tokens.append(matchobj.group(1))
return "([^/]+)"

key2 = KEY_MATCH4_PATTERN.sub(repl, key2)

regexp = re.compile("^" + key2 + "$")
matches = regexp.match(key1)

if matches is None:
return False
if len(tokens) != len(matches.groups()):
raise Exception("KeyMatch4: number of tokens is not equal to number of values")

tokens_matches = dict()

for i in range(len(tokens)):
token, match = tokens[i], matches.groups()[i]

if token not in tokens_matches.keys():
tokens_matches[token] = match
else:
if tokens_matches[token] != match:
return False
return True


def key_match4_func(*args) -> bool:
"""
key_match4_func is the wrapper for key_match4.
"""
name1 = args[0]
name2 = args[1]

return key_match4(name1, name2)


def regex_match(key1, key2):
"""determines whether key1 matches the pattern of key2 in regular expression."""

Expand Down
49 changes: 49 additions & 0 deletions tests/util/test_builtin_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,55 @@ def test_key_match3(self):
util.key_match3_func("/myid/using/myresid", "/{id/using/{resId}")
)

def test_key_match4(self):
self.assertTrue(
util.key_match4_func("/parent/123/child/123", "/parent/{id}/child/{id}")
)
self.assertFalse(
util.key_match4_func("/parent/123/child/456", "/parent/{id}/child/{id}")
)

self.assertTrue(
util.key_match4_func(
"/parent/123/child/123", "/parent/{id}/child/{another_id}"
)
)
self.assertTrue(
util.key_match4_func(
"/parent/123/child/456", "/parent/{id}/child/{another_id}"
)
)

self.assertTrue(
util.key_match4_func(
"/parent/123/child/456", "/parent/{id}/child/{another_id}"
)
)
self.assertFalse(
util.key_match4_func(
"/parent/123/child/123/book/456", "/parent/{id}/child/{id}/book/{id}"
)
)
self.assertFalse(
util.key_match4_func(
"/parent/123/child/456/book/123", "/parent/{id}/child/{id}/book/{id}"
)
)
self.assertFalse(
util.key_match4_func(
"/parent/123/child/456/book/", "/parent/{id}/child/{id}/book/{id}"
)
)
self.assertFalse(
util.key_match4_func(
"/parent/123/child/456", "/parent/{id}/child/{id}/book/{id}"
)
)

self.assertFalse(
util.key_match4_func("/parent/123/child/123", "/parent/{i/d}/child/{i/d}")
)

def test_regex_match(self):
self.assertTrue(util.regex_match_func("/topic/create", "/topic/create"))
self.assertTrue(util.regex_match_func("/topic/create/123", "/topic/create"))
Expand Down

0 comments on commit ef127b3

Please sign in to comment.