Skip to content

Commit

Permalink
feat: add glob pattern to built-in function
Browse files Browse the repository at this point in the history
  • Loading branch information
nodece committed Apr 3, 2020
1 parent 6be5cef commit 8415fc2
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 2 deletions.
11 changes: 11 additions & 0 deletions examples/glob_model.conf
@@ -0,0 +1,11 @@
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && globMatch(r.obj, p.obj) && r.act == p.act
4 changes: 4 additions & 0 deletions examples/glob_policy.csv
@@ -0,0 +1,4 @@
p, u1, /foo/*, read
p, u2, /foo*, read
p, u3, /*/foo/*, read
p, u4, *, read
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -19,6 +19,7 @@
"@types/ip": "^0.0.31",
"@types/jest": "^24.0.11",
"@types/lodash": "^4.14.113",
"@types/micromatch": "^4.0.1",
"@types/node": "^10.5.3",
"@typescript-eslint/eslint-plugin": "^2.6.1",
"@typescript-eslint/parser": "^2.6.1",
Expand All @@ -42,7 +43,8 @@
"await-lock": "^2.0.1",
"expression-eval": "^2.0.0",
"ip": "^1.1.5",
"lodash": "^4.17.15"
"lodash": "^4.17.15",
"micromatch": "^4.0.2"
},
"files": [
"lib",
Expand Down
1 change: 1 addition & 0 deletions src/model/functionMap.ts
Expand Up @@ -35,6 +35,7 @@ export class FunctionMap {
fm.addFunction('keyMatch4', util.keyMatch4Func);
fm.addFunction('regexMatch', util.regexMatchFunc);
fm.addFunction('ipMatch', util.ipMatchFunc);
fm.addFunction('globMatch', util.globMatch);

return fm;
}
Expand Down
20 changes: 19 additions & 1 deletion src/util/builtinOperators.ts
Expand Up @@ -15,6 +15,7 @@
import * as rbac from '../rbac';
import * as ip from 'ip';
import * as _ from 'lodash';
import { isMatch } from 'micromatch';

// regexMatch determines whether key1 matches the pattern of key2 in regular expression.
function regexMatch(key1: string, key2: string): boolean {
Expand Down Expand Up @@ -208,6 +209,23 @@ function ipMatchFunc(...args: any[]): boolean {
return ipMatch(ip1, ip2);
}

/**
* Returns true if the specified `string` matches the given glob `pattern`.
*
* @param string String to match
* @param pattern Glob pattern to use for matching.
* @returns Returns true if the string matches the glob pattern.
*
* @example
* ```javascript
* globMatch("abc.conf", "*.conf") => true
* ```
*/
function globMatch(string: string, pattern: string): boolean {
const ok = isMatch(string, pattern);
return ok;
}

// generateGFunction is the factory method of the g(_, _) function.
function generateGFunction(rm: rbac.RoleManager): any {
return async function func(...args: any[]): Promise<boolean> {
Expand All @@ -225,4 +243,4 @@ function generateGFunction(rm: rbac.RoleManager): any {
};
}

export { keyMatchFunc, keyMatch2Func, keyMatch3Func, regexMatchFunc, ipMatchFunc, generateGFunction, keyMatch4Func };
export { keyMatchFunc, keyMatch2Func, keyMatch3Func, regexMatchFunc, ipMatchFunc, generateGFunction, keyMatch4Func, globMatch };
18 changes: 18 additions & 0 deletions test/model.test.ts
Expand Up @@ -155,6 +155,24 @@ class TestResource {
}
}

test('TestGlobMatchModel', async () => {
const e = await newEnforcer('examples/glob_model.conf', 'examples/glob_policy.csv');

await testEnforce(e, 'u1', '/foo', 'read', false);
await testEnforce(e, 'u1', '/foo/subprefix', 'read', true);
await testEnforce(e, 'u1', 'foo', 'read', false);

await testEnforce(e, 'u2', '/foosubprefix', 'read', true);
await testEnforce(e, 'u2', '/foo/subprefix', 'read', false);
await testEnforce(e, 'u2', 'foo', 'read', false);

await testEnforce(e, 'u3', '/prefix/foo/subprefix', 'read', true);
await testEnforce(e, 'u3', '/prefix/foo', 'read', false);

await testEnforce(e, 'u4', '/foo', 'read', false);
await testEnforce(e, 'u4', 'foo', 'read', true);
});

test('TestABACModel', async () => {
const e = await newEnforcer('examples/abac_model.conf');

Expand Down
46 changes: 46 additions & 0 deletions test/util.test.ts
Expand Up @@ -87,3 +87,49 @@ test('test ipMatchFunc', () => {
expect(() => util.ipMatchFunc('127.0.0.1', 'I am alice')).toThrow(/invalid/g);
expect(util.ipMatchFunc('192.168.2.189', '192.168.1.134/26')).toEqual(false);
});

test('test globMatch', () => {
expect(util.globMatch('/foo', '/foo')).toEqual(true);
expect(util.globMatch('/foo', '/foo*')).toEqual(true);
expect(util.globMatch('/foo', '/foo/*')).toEqual(false);

expect(util.globMatch('/foo', '/foo')).toEqual(true);
expect(util.globMatch('/foo', '/foo*')).toEqual(true);
expect(util.globMatch('/foo', '/foo/*')).toEqual(false);
expect(util.globMatch('/foo/bar', '/foo')).toEqual(false);
expect(util.globMatch('/foo/bar', '/foo*')).toEqual(false);
expect(util.globMatch('/foo/bar', '/foo/*')).toEqual(true);
expect(util.globMatch('/foobar', '/foo')).toEqual(false);
expect(util.globMatch('/foobar', '/foo*')).toEqual(true);
expect(util.globMatch('/foobar', '/foo/*')).toEqual(false);

expect(util.globMatch('/foo', '*/foo')).toEqual(true);
expect(util.globMatch('/foo', '*/foo*')).toEqual(true);
expect(util.globMatch('/foo', '*/foo/*')).toEqual(false);
expect(util.globMatch('/foo/bar', '*/foo')).toEqual(false);
expect(util.globMatch('/foo/bar', '*/foo*')).toEqual(false);
expect(util.globMatch('/foo/bar', '*/foo/*')).toEqual(true);
expect(util.globMatch('/foobar', '*/foo')).toEqual(false);
expect(util.globMatch('/foobar', '*/foo*')).toEqual(true);
expect(util.globMatch('/foobar', '*/foo/*')).toEqual(false);

expect(util.globMatch('/prefix/foo', '*/foo')).toEqual(false);
expect(util.globMatch('/prefix/foo', '*/foo*')).toEqual(false);
expect(util.globMatch('/prefix/foo', '*/foo/*')).toEqual(false);
expect(util.globMatch('/prefix/foo/bar', '*/foo')).toEqual(false);
expect(util.globMatch('/prefix/foo/bar', '*/foo*')).toEqual(false);
expect(util.globMatch('/prefix/foo/bar', '*/foo/*')).toEqual(false);
expect(util.globMatch('/prefix/foobar', '*/foo')).toEqual(false);
expect(util.globMatch('/prefix/foobar', '*/foo*')).toEqual(false);
expect(util.globMatch('/prefix/foobar', '*/foo/*')).toEqual(false);

expect(util.globMatch('/prefix/subprefix/foo', '*/foo')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foo', '*/foo*')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foo', '*/foo/*')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foo/bar', '*/foo')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foo/bar', '*/foo*')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foo/bar', '*/foo/*')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foobar', '*/foo')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foobar', '*/foo*')).toEqual(false);
expect(util.globMatch('/prefix/subprefix/foobar', '*/foo/*')).toEqual(false);
});
12 changes: 12 additions & 0 deletions yarn.lock
Expand Up @@ -545,6 +545,11 @@
dependencies:
"@babel/types" "^7.3.0"

"@types/braces@*":
version "3.0.0"
resolved "https://registry.npm.taobao.org/@types/braces/download/@types/braces-3.0.0.tgz#7da1c0d44ff1c7eb660a36ec078ea61ba7eb42cb"
integrity sha1-faHA1E/xx+tmCjbsB46mG6frQss=

"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.npm.taobao.org/@types/color-name/download/@types/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
Expand Down Expand Up @@ -618,6 +623,13 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.135.tgz#d2607c35dd68f70c2b35ba020c667493dedd8447"
integrity sha512-Ed+tSZ9qM1oYpi5kzdsBuOzcAIn1wDW+e8TFJ50IMJMlSopGdJgKAbhHzN6h1E1OfjlGOr2JepzEWtg9NIfoNg==

"@types/micromatch@^4.0.1":
version "4.0.1"
resolved "https://registry.npm.taobao.org/@types/micromatch/download/@types/micromatch-4.0.1.tgz#9381449dd659fc3823fd2a4190ceacc985083bc7"
integrity sha1-k4FEndZZ/Dgj/SpBkM6syYUIO8c=
dependencies:
"@types/braces" "*"

"@types/minimatch@*", "@types/minimatch@^3.0.3":
version "3.0.3"
resolved "https://registry.npm.taobao.org/@types/minimatch/download/@types/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
Expand Down

0 comments on commit 8415fc2

Please sign in to comment.