feat: add pattern model

Signed-off-by: Zixuan Liu <nodeces@gmail.com>
This commit is contained in:
Zixuan Liu 2020-10-09 15:37:23 +08:00
parent 3fb1f91e6e
commit 207a5a23c2
13 changed files with 11781 additions and 402 deletions

1
.gitignore vendored
View File

@ -22,4 +22,3 @@ dist
build
package-lock.json
yarn.lock

View File

@ -6,7 +6,7 @@
"keywords": [],
"main": "src/index.js",
"dependencies": {
"casbin": "^5.0.5",
"casbin": "^5.1.6",
"codemirror": "5.48.4",
"normalize.css": "^8.0.1",
"react": "16.12.0",

View File

@ -1,205 +0,0 @@
/* eslint-disable */
const exampleModel = {
/////////////////////////////////////////////////////////////////////////
basic:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.sub == p.sub && r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
basic_with_root:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"',
/////////////////////////////////////////////////////////////////////////
basic_without_resources:
'[request_definition]\n' +
'r = sub, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.sub == p.sub && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
basic_without_users:
'[request_definition]\n' +
'r = obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
rbac:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[role_definition]\n' +
'g = _, _\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
rbac_with_resource_roles:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[role_definition]\n' +
'g = _, _\n' +
'g2 = _, _\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
rbac_with_domains:
'[request_definition]\n' +
'r = sub, dom, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, dom, obj, act\n' +
'\n' +
'[role_definition]\n' +
'g = _, _, _\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
rbac_with_deny:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act, eft\n' +
'\n' +
'[role_definition]\n' +
'g = _, _\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow)) && !some(where (p.eft == deny))\n' +
'\n' +
'[matchers]\n' +
'm = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
abac:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.sub == r.obj.Owner',
/////////////////////////////////////////////////////////////////////////
abac_with_policy_rule:
'[request_definition]\n'+
'r = sub, obj, act\n'+
'\n'+
'[policy_definition]\n'+
'p = sub_rule, obj, act\n'+
'\n'+
'[policy_effect]\n'+
'e = some(where (p.eft == allow))\n'+
'\n'+
'[matchers]\n'+
'm = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
keymatch:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)',
/////////////////////////////////////////////////////////////////////////
keymatch2:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)',
/////////////////////////////////////////////////////////////////////////
ipmatch:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act\n' +
'\n' +
'[policy_effect]\n' +
'e = some(where (p.eft == allow))\n' +
'\n' +
'[matchers]\n' +
'm = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.act',
/////////////////////////////////////////////////////////////////////////
priority:
'[request_definition]\n' +
'r = sub, obj, act\n' +
'\n' +
'[policy_definition]\n' +
'p = sub, obj, act, eft\n' +
'\n' +
'[role_definition]\n' +
'g = _, _\n' +
'\n' +
'[policy_effect]\n' +
'e = priority(p.eft) || deny\n' +
'\n' +
'[matchers]\n' +
'm = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act'
/////////////////////////////////////////////////////////////////////////
};
export default exampleModel;

View File

@ -1,81 +0,0 @@
/* eslint-disable */
const examplePolicy = {
/////////////////////////////////////////////////////////////////////////
basic: 'p, alice, data1, read\n' + 'p, bob, data2, write',
/////////////////////////////////////////////////////////////////////////
basic_with_root: 'p, alice, data1, read\n' + 'p, bob, data2, write',
/////////////////////////////////////////////////////////////////////////
basic_without_resources: 'p, alice, read\n' + 'p, bob, write',
/////////////////////////////////////////////////////////////////////////
basic_without_users: 'p, data1, read\n' + 'p, data2, write',
/////////////////////////////////////////////////////////////////////////
rbac:
'p, alice, data1, read\n' +
'p, bob, data2, write\n' +
'p, data2_admin, data2, read\n' +
'p, data2_admin, data2, write\n' +
'\n' +
'g, alice, data2_admin',
/////////////////////////////////////////////////////////////////////////
rbac_with_resource_roles:
'p, alice, data1, read\n' +
'p, bob, data2, write\n' +
'p, data_group_admin, data_group, write\n' +
'\n' +
'g, alice, data_group_admin\n' +
'g2, data1, data_group\n' +
'g2, data2, data_group',
/////////////////////////////////////////////////////////////////////////
rbac_with_domains:
'p, admin, domain1, data1, read\n' +
'p, admin, domain1, data1, write\n' +
'p, admin, domain2, data2, read\n' +
'p, admin, domain2, data2, write\n' +
'\n' +
'g, alice, admin, domain1\n' +
'g, bob, admin, domain2',
/////////////////////////////////////////////////////////////////////////
rbac_with_deny:
'p, alice, data1, read, allow\n' +
'p, bob, data2, write, allow\n' +
'p, data2_admin, data2, read, allow\n' +
'p, data2_admin, data2, write, allow\n' +
'p, alice, data2, write, deny\n' +
'\n' +
'g, alice, data2_admin',
/////////////////////////////////////////////////////////////////////////
abac: '',
/////////////////////////////////////////////////////////////////////////
abac_with_policy_rule: 'p, r.sub.Age > 18 && r.sub.Age < 60, /data1, read\n',
/////////////////////////////////////////////////////////////////////////
keymatch:
'p, alice, /alice_data/*, GET\n' +
'p, alice, /alice_data/resource1, POST\n' +
'\n' +
'p, bob, /alice_data/resource2, GET\n' +
'p, bob, /bob_data/*, POST\n' +
'\n' +
'p, cathy, /cathy_data, (GET)|(POST)',
/////////////////////////////////////////////////////////////////////////
keymatch2: 'p, alice, /alice_data/:resource, GET\n' + 'p, alice, /alice_data2/:id/using/:resId, GET',
/////////////////////////////////////////////////////////////////////////
ipmatch: 'p, 192.168.2.0/24, data1, read\n' + 'p, 10.0.0.0/16, data2, write',
/////////////////////////////////////////////////////////////////////////
priority:
'p, alice, data1, read, allow\n' +
'p, data1_deny_group, data1, read, deny\n' +
'p, data1_deny_group, data1, write, deny\n' +
'p, alice, data1, write, allow\n' +
'\n' +
'g, alice, data1_deny_group\n' +
'\n' +
'p, data2_allow_group, data2, read, allow\n' +
'p, bob, data2, read, deny\n' +
'p, bob, data2, write, deny\n' +
'\n' +
'g, bob, data2_allow_group'
/////////////////////////////////////////////////////////////////////////
};
export default examplePolicy;

View File

@ -1,19 +0,0 @@
/* eslint-disable */
const exampleRequest = {
basic: 'alice, data1, read',
basic_with_root: 'alice, data1, read',
basic_without_resources: 'alice, read',
basic_without_users: 'data1, read',
rbac: 'alice, data2, read',
rbac_with_resource_roles: 'alice, data1, read\n' + 'alice, data1, write\n' + 'alice, data2, read\n' + 'alice, data2, write ',
rbac_with_domains: 'alice, domain1, data1, read',
rbac_with_deny: 'alice, data1, read\n' + 'alice, data2, write',
abac: `alice, { Owner: 'alice'}\n` + `alice, { Owner: 'bob'}`,
abac_with_policy_rule: `{ Age: 30}, /data1, read`,
keymatch: 'alice, /alice_data/hello, GET',
keymatch2: 'alice, /alice_data/hello, GET\n' + 'alice, /alice_data/hello, POST',
ipmatch: '192.168.2.1, data1, read\n' + '10.0.2.3, data2, write',
priority: 'alice, data1, read'
};
export default exampleRequest;

View File

@ -0,0 +1,414 @@
export const example = {
basic: {
name: 'ACL',
model: `[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 && r.obj == p.obj && r.act == p.act`,
policy: `p, alice, data1, read
p, bob, data2, write`,
request: `alice, data1, read`,
customConfig: undefined
},
basic_with_root: {
name: 'ACL with superuser',
model: `[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 && r.obj == p.obj && r.act == p.act || r.sub == "root"`,
policy: `p, alice, data1, read
p, bob, data2, write`,
request: `alice, data1, read`,
customConfig: undefined
},
basic_without_resources: {
name: 'ACL without resources',
model: `[request_definition]
r = sub, act
[policy_definition]
p = sub, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.act == p.act`,
policy: `p, alice, read
p, bob, write`,
request: `alice, read`,
customConfig: undefined
},
basic_without_users: {
name: 'ACL without users',
model: `[request_definition]
r = obj, act
[policy_definition]
p = obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.obj == p.obj && r.act == p.act`,
policy: `p, data1, read
p, data2, write`,
request: `data1, read`,
customConfig: undefined
},
rbac: {
name: 'RBAC',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
policy: `p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin`,
request: `alice, data2, read`,
customConfig: undefined
},
rbac_with_resource_roles: {
name: 'RBAC with resource roles',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
g2 = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act`,
policy: `p, alice, data1, read
p, bob, data2, write
p, data_group_admin, data_group, write
g, alice, data_group_admin
g2, data1, data_group
g2, data2, data_group`,
request: `alice, data1, read
alice, data1, write
alice, data2, read
alice, data2, write `,
customConfig: undefined
},
rbac_with_domains: {
name: 'RBAC with domains/tenants',
model: `[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act`,
policy: `p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write
g, alice, admin, domain1
g, bob, admin, domain2`,
request: `alice, domain1, data1, read`,
customConfig: undefined
},
rbac_with_pattern: {
name: 'RBAC with pattern',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && regexMatch(r.act, p.act)`,
policy: `
p, pen_admin, data1, GET
g, /book/:id, book_admin
`,
request: `/book/1, data1, GET`,
customConfig: `
(function() {
return {
/**
* Here is custom functions for Casbin.
* Currently, there are built-in globMatch, keyMatch, keyMatch2, keyMatch3, keyMatch4, regexMatch, ipMatch.
*/
functions: {},
/**
* The value comes from config.functions, Casbin will not use this configuration if the value is undefined.
* example:
* matchingForGFunction: 'globMatch'
* matchingDomainForGFunction: 'keyMatch'
*/
matchingForGFunction: 'keyMatch2',
matchingDomainForGFunction: undefined
};
})();`
},
rbac_with_all_pattern: {
name: 'RBAC with all pattern',
model: `[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act`,
policy: `p, book_group, domain1, data1, read
p, book_group, domain2, data2, write
g, /book/:id, book_group, *`,
request: `/book/1, domain1, data1, read
/book/1, domain2, data2, write
`,
customConfig: `
(function() {
return {
/**
* Here is custom functions for Casbin.
* Currently, there are built-in globMatch, keyMatch, keyMatch2, keyMatch3, keyMatch4, regexMatch, ipMatch.
*/
functions: {},
/**
* The value comes from config.functions, Casbin will not use this configuration if the value is undefined.
* example:
* matchingForGFunction: 'globMatch'
* matchingDomainForGFunction: 'keyMatch'
*/
matchingForGFunction: 'keyMatch2',
matchingDomainForGFunction: 'keyMatch2'
};
})();`
},
rbac_with_deny: {
name: 'RBAC with deny-override',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, eft
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
policy: `p, alice, data1, read, allow
p, bob, data2, write, allow
p, data2_admin, data2, read, allow
p, data2_admin, data2, write, allow
p, alice, data2, write, deny
g, alice, data2_admin`,
request: `alice, data1, read
alice, data2, write`,
customConfig: undefined
},
abac: {
name: 'ABAC',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == r.obj.Owner`,
policy: '',
request: `alice, { Owner: 'alice'}
alice, { Owner: 'bob'}`,
customConfig: undefined
},
abac_with_policy_rule: {
name: 'ABAC with policy rule',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub_rule, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act`,
policy: `p, r.sub.Age > 18 && r.sub.Age < 60, /data1, read
`,
request: `{ Age: 30}, /data1, read`,
customConfig: undefined
},
keymatch: {
name: 'RESTful (KeyMatch)',
model: `[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 && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)`,
policy: `p, alice, /alice_data/*, GET
p, alice, /alice_data/resource1, POST
p, bob, /alice_data/resource2, GET
p, bob, /bob_data/*, POST
p, cathy, /cathy_data, (GET)|(POST)`,
request: 'alice, /alice_data/hello, GET',
customConfig: undefined
},
keymatch2: {
name: 'RESTful (KeyMatch2)',
model: `[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 && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)`,
policy: `p, alice, /alice_data/:resource, GET
p, alice, /alice_data2/:id/using/:resId, GET`,
request: `alice, /alice_data/hello, GET
alice, /alice_data/hello, POST`,
customConfig: undefined
},
ipmatch: {
name: 'IP match',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
policy: `p, 192.168.2.0/24, data1, read
p, 10.0.0.0/16, data2, write`,
request: `192.168.2.1, data1, read
10.0.2.3, data2, write`,
customConfig: undefined
},
priority: {
name: 'Priority',
model: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, eft
[role_definition]
g = _, _
[policy_effect]
e = priority(p.eft) || deny
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
policy: `p, alice, data1, read, allow
p, data1_deny_group, data1, read, deny
p, data1_deny_group, data1, write, deny
p, alice, data1, write, allow
g, alice, data1_deny_group
p, data2_allow_group, data2, read, allow
p, bob, data2, read, deny
p, bob, data2, write, deny
g, bob, data2_allow_group`,
request: `alice, data1, read`,
customConfig: undefined
}
};
export const defaultCustomConfig = `(function() {
return {
/**
* Here is custom functions for Casbin.
* Currently, there are built-in globMatch, keyMatch, keyMatch2, keyMatch3, keyMatch4, regexMatch, ipMatch.
*/
functions: {},
/**
* The value comes from config.functions, Casbin will not use this configuration if the value is undefined.
* example:
* matchingForGFunction: 'globMatch'
* matchingDomainForGFunction: 'keyMatch'
*/
matchingForGFunction: undefined,
matchingDomainForGFunction: undefined
};
})();`
export type Example = typeof example;
export type ModelKind = keyof Example;

View File

@ -11,9 +11,10 @@ import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/display/placeholder';
import './casbin-mode/casbin-conf';
import './casbin-mode/casbin-csv';
import { ModelKind } from './casbin-mode/example';
interface CasbinCodeMirror {
modelKind: string;
modelKind: ModelKind;
options: codemirror.EditorConfiguration;
style?: CSSProperties;
onChange: (text: string) => void;
@ -21,7 +22,7 @@ interface CasbinCodeMirror {
}
interface EditorProps {
modelKind: string;
modelKind: ModelKind;
onChange?: (text: string) => void;
style?: CSSProperties;
}

View File

@ -1,21 +1,28 @@
import React, { isValidElement, useState } from 'react';
import React, { isValidElement, useEffect, useState } from 'react';
import SelectModel from './select-model';
import { Button, EditorContainer, FlexRow, HeaderTitle } from '../ui';
import { getSelectedModel, reset } from './persist';
import { CustomFunctionEditor, ModelEditor, PolicyEditor, RequestEditor, RequestResultEditor } from './editor';
import Syntax from './syntax';
import RunTest from './run-test';
import { ModelKind } from './casbin-mode/example';
export const EditorScreen = () => {
const [modelKind, setModelKind] = useState(getSelectedModel());
const [modelKind, setModelKind] = useState<ModelKind>(getSelectedModel());
const [modelText, setModelText] = useState('');
const [policy, setPolicy] = useState('');
const [fn, setFn] = useState('');
const [customConfig, setCustomConfig] = useState('');
const [request, setRequest] = useState('');
const [echo, setEcho] = useState<JSX.Element>(<></>);
const [requestResult, setRequestResult] = useState('');
const [visible, setVisible] = useState(false);
const [customConfigVisible, setCustomConfigVisible] = useState(true);
useEffect(() => {
// Automatically hide the custom config container.
setTimeout(() => {
setCustomConfigVisible(false);
}, 2000);
});
return (
<>
<FlexRow>
@ -24,7 +31,7 @@ export const EditorScreen = () => {
<HeaderTitle>Model</HeaderTitle>
<SelectModel
onChange={value => {
setModelKind(value);
setModelKind(value as ModelKind);
}}
/>
<Button
@ -62,10 +69,10 @@ export const EditorScreen = () => {
<FlexRow>
<EditorContainer>
<FlexRow>
<HeaderTitle>Custom Function</HeaderTitle>
<Button onClick={() => setVisible(!visible)}>TOGGLE</Button>
<HeaderTitle>Custom Config</HeaderTitle>
<Button onClick={() => setCustomConfigVisible(!customConfigVisible)}>TOGGLE</Button>
</FlexRow>
{visible && <CustomFunctionEditor modelKind={modelKind} onChange={setFn} />}
{customConfigVisible && <CustomFunctionEditor modelKind={modelKind} onChange={setCustomConfig} />}
</EditorContainer>
</FlexRow>
@ -75,7 +82,7 @@ export const EditorScreen = () => {
modelKind={modelKind}
model={modelText}
policy={policy}
fn={fn}
customConfig={customConfig}
request={request}
onResponse={v => {
if (isValidElement(v)) {

View File

@ -1,8 +1,6 @@
import exampleModel from './casbin-mode/example-model';
import examplePolicy from './casbin-mode/example-policy';
import exampleRequest from './casbin-mode/example-request';
import { defaultCustomConfig, example, ModelKind } from './casbin-mode/example';
export const DEFAULT_MODEL = 'basic';
export const DEFAULT_MODEL: ModelKind = 'basic';
export enum Persist {
MODEL,
@ -15,30 +13,32 @@ function getKey(persist: Persist, modelName: string) {
return `${modelName.toUpperCase()}_${Persist[persist]}`;
}
export function getSelectedModel() {
const v = window.localStorage.getItem(Persist.MODEL.toString());
return v ? v : DEFAULT_MODEL;
export function getSelectedModel(): ModelKind {
const v = window.localStorage.getItem(Persist[Persist.MODEL].toString());
return (v ? v : DEFAULT_MODEL) as ModelKind;
}
export function setSelectedModel(value: string) {
window.localStorage.setItem(Persist[Persist.MODEL], value);
}
export function get(persist: Persist, modelName = DEFAULT_MODEL) {
export function get(persist: Persist, modelName: ModelKind = DEFAULT_MODEL) {
const data = window.localStorage.getItem(getKey(persist, modelName));
if (data) {
return data;
}
const m = example[modelName];
switch (persist) {
case Persist.MODEL:
return (exampleModel as any)[modelName];
return m.model;
case Persist.POLICY:
return (examplePolicy as any)[modelName];
return m.policy;
case Persist.REQUEST:
return (exampleRequest as any)[modelName];
return m.request;
case Persist.CUSTOM_FUNCTION:
return `var fns = {}`;
return m.customConfig || defaultCustomConfig;
}
}

View File

@ -1,17 +1,17 @@
import React from 'react';
import { Button, Echo } from '../ui';
import { newEnforcer, newModel, StringAdapter } from 'casbin';
import { DefaultRoleManager, newEnforcer, newModel, StringAdapter, Util } from 'casbin';
interface RunTestProps {
model: string;
modelKind: string;
policy: string;
fn: string;
customConfig: string;
request: string;
onResponse: (com: JSX.Element | any[]) => void;
}
const needsAbacParsing = new Set(['abac','abac_with_policy_rule']);
const needsAbacParsing = new Set(['abac', 'abac_with_policy_rule']);
function parseABACRequest(line: string): any[] {
let value = '';
@ -27,7 +27,7 @@ function parseABACRequest(line: string): any[] {
if (objectToken === 0 && line[i] === ',') {
if (parseToObject) {
// eslint-disable-next-line
eval(`value = ${value}`);
value = eval(`(${value})`);
}
request.push(value);
@ -56,7 +56,7 @@ function parseABACRequest(line: string): any[] {
if (value) {
if (parseToObject) {
// eslint-disable-next-line
eval(`value = ${value}`);
value = eval(`(${value})`);
}
request.push(value);
}
@ -64,59 +64,105 @@ function parseABACRequest(line: string): any[] {
return request;
}
async function enforcer(props: RunTestProps) {
debugger;
const startTime = performance.now();
const result = [];
try {
const e = await newEnforcer(newModel(props.model), props.policy ? new StringAdapter(props.policy) : undefined);
const customConfigCode = props.customConfig;
if (customConfigCode) {
try {
const builtinFunc = {
keyMatch: Util.keyMatchFunc,
keyMatch2: Util.keyMatch2Func,
keyMatch3: Util.keyMatch3Func,
keyMatch4: Util.keyMatch4Func,
regexMatch: Util.regexMatchFunc,
ipMatch: Util.ipMatchFunc,
globMatch: Util.globMatch
};
// eslint-disable-next-line
let config = eval(customConfigCode);
if (config) {
config = { ...config, functions: { ...config.functions, ...builtinFunc } };
if (config?.functions) {
Object.keys(config.functions).forEach(key => e.addFunction(key, config.functions[key]));
}
const rm = e.getRoleManager() as DefaultRoleManager;
const matchingForGFunction = config.matchingForGFunction;
if (matchingForGFunction) {
if (typeof matchingForGFunction === 'function') {
await rm.addMatchingFunc(matchingForGFunction);
}
if (typeof matchingForGFunction === 'string') {
if (matchingForGFunction in config.functions) {
console.warn('add matchingForGFunction');
await rm.addMatchingFunc(config.functions[matchingForGFunction]);
} else {
props.onResponse(<Echo type={'error'}>Must sure the {matchingForGFunction}() in config.functions</Echo>);
return;
}
}
}
const matchingDomainForGFunction = config.matchingDomainForGFunction;
if (matchingDomainForGFunction) {
if (typeof matchingDomainForGFunction === 'function') {
console.warn('add addDomainMatchingFunc');
await rm.addDomainMatchingFunc(matchingDomainForGFunction);
}
if (typeof matchingDomainForGFunction === 'string') {
if (matchingDomainForGFunction in config.functions) {
console.warn('add addDomainMatchingFunc');
await rm.addDomainMatchingFunc(config.functions[matchingDomainForGFunction]);
} else {
props.onResponse(<Echo type={'error'}>Must sure the {matchingDomainForGFunction}() in config.functions</Echo>);
return;
}
}
}
}
} catch (e) {
props.onResponse(<Echo type={'error'}>Please check syntax in Custom Function Editor: {e.message}</Echo>);
return;
}
}
const requests = props.request.split('\n');
for (const n of requests) {
const line = n.trim();
if (!line) {
result.push('// ignore');
continue;
}
if (line[0] === '#') {
result.push('// ignore');
continue;
}
const rvals = needsAbacParsing.has(props.modelKind) ? parseABACRequest(n) : n.split(',').map(n => n.trim());
result.push(await e.enforce(...rvals));
}
const stopTime = performance.now();
props.onResponse(<Echo>{'Done in ' + ((stopTime - startTime) / 1000.0).toFixed(2) + 's'}</Echo>);
props.onResponse(result);
} catch (e) {
props.onResponse(<Echo type={'error'}>{e.message}</Echo>);
props.onResponse([]);
}
}
const RunTest = (props: RunTestProps) => {
return (
<Button
style={{ marginRight: 8 }}
onClick={async () => {
const startTime = performance.now();
const result = [];
try {
const e = await newEnforcer(newModel(props.model), props.policy ? new StringAdapter(props.policy) : undefined);
const fnString = props.fn;
if (fnString) {
try {
const fns: any = {};
// eslint-disable-next-line
eval(`${fnString}`);
if (fns) {
Object.keys(fns).forEach(key => e.addFunction(key, fns[key]));
}
} catch (e) {
props.onResponse(<Echo>Please check syntax in Custom Function Editor.</Echo>);
return;
}
}
const requests = props.request.split('\n');
for (const n of requests) {
const line = n.trim();
if (!line) {
result.push('# ignore');
continue;
}
if (line[0] === '#') {
result.push('# ignore');
continue;
}
const rvals = needsAbacParsing.has( props.modelKind) ? parseABACRequest(n) : n.split(',').map(n => n.trim());
result.push(await e.enforce(...rvals));
}
const stopTime = performance.now();
props.onResponse(<Echo>{'Done in ' + ((stopTime - startTime) / 1000.0).toFixed(2) + 's'}</Echo>);
props.onResponse(result);
} catch (e) {
props.onResponse(<Echo type={'error'}>{e.message}</Echo>);
props.onResponse([]);
}
}}
>
<Button style={{ marginRight: 8 }} onClick={() => enforcer(props)}>
RUN THE TEST
</Button>
);

View File

@ -1,5 +1,6 @@
import React from 'react';
import { getSelectedModel, setSelectedModel } from './persist';
import { ModelKind, example } from './casbin-mode/example';
interface SelectModelProps {
onChange: (value: string) => void;
@ -18,20 +19,11 @@ const SelectModel = (props: SelectModelProps) => {
<option value="" disabled>
Select your model
</option>
<option value="basic">ACL</option>
<option value="basic_with_root">ACL with superuser</option>
<option value="basic_without_resources">ACL without resources</option>
<option value="basic_without_users">ACL without users</option>
<option value="rbac">RBAC</option>
<option value="rbac_with_resource_roles">RBAC with resource roles</option>
<option value="rbac_with_domains">RBAC with domains/tenants</option>
<option value="rbac_with_deny">RBAC with deny-override</option>
<option value="abac">ABAC</option>
<option value="abac_with_policy_rule">ABAC with policy rule</option>
<option value="keymatch">RESTful (KeyMatch)</option>
<option value="keymatch2">RESTful (KeyMatch2)</option>
<option value="ipmatch">IP match</option>
<option value="priority">Priority</option>
{Object.keys(example).map(n => (
<option key={n} value={n}>
{example[n as ModelKind].name}
</option>
))}
</select>
);
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Button, Echo } from '../ui';
import { Config } from 'casbin/lib/config';
import { Config } from 'casbin';
interface SyntaxProps {
model: string;
@ -14,7 +14,7 @@ const Syntax = (props: SyntaxProps) => {
onClick={() => {
try {
Config.newConfigFromText(props.model);
props.onResponse(<Echo>passed</Echo>);
props.onResponse(<Echo>Passed</Echo>);
} catch (e) {
props.onResponse(<Echo type={'error'}>{e.message}</Echo>);
}

11225
yarn.lock Normal file

File diff suppressed because it is too large Load Diff