Skip to content

Commit

Permalink
feat: add enforce context (#88)
Browse files Browse the repository at this point in the history
Signed-off-by: nodece <nodeces@gmail.com>
  • Loading branch information
nodece committed May 3, 2023
1 parent e13f7f9 commit c42450d
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 29 deletions.
64 changes: 44 additions & 20 deletions src/editor/casbin-mode/example.ts
@@ -1,5 +1,8 @@
/* eslint-disable @typescript-eslint/camelcase */
export const example = {
export const example: Record<
string,
{ name: string; model: string; policy: string; request: string; customConfig?: string; enforceContext?: string }
> = {
basic: {
name: 'ACL',
model: `[request_definition]
Expand All @@ -16,7 +19,8 @@ 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
customConfig: undefined,
enforceContext: undefined
},
basic_with_root: {
name: 'ACL with superuser',
Expand All @@ -34,7 +38,8 @@ 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
customConfig: undefined,
enforceContext: undefined
},
basic_without_resources: {
name: 'ACL without resources',
Expand All @@ -52,7 +57,8 @@ m = r.sub == p.sub && r.act == p.act`,
policy: `p, alice, read
p, bob, write`,
request: `alice, read`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
basic_without_users: {
name: 'ACL without users',
Expand All @@ -70,7 +76,8 @@ m = r.obj == p.obj && r.act == p.act`,
policy: `p, data1, read
p, data2, write`,
request: `data1, read`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
rbac: {
name: 'RBAC',
Expand All @@ -95,7 +102,8 @@ p, data2_admin, data2, write
g, alice, data2_admin`,
request: `alice, data2, read`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
rbac_with_resource_roles: {
name: 'RBAC with resource roles',
Expand Down Expand Up @@ -125,8 +133,8 @@ g2, data2, data_group`,
alice, data1, write
alice, data2, read
alice, data2, write `,

customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
rbac_with_domains: {
name: 'RBAC with domains/tenants',
Expand All @@ -152,7 +160,8 @@ p, admin, domain2, data2, write
g, alice, admin, domain1
g, bob, admin, domain2`,
request: `alice, domain1, data1, read`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
rbac_with_pattern: {
name: 'RBAC with pattern',
Expand Down Expand Up @@ -192,7 +201,8 @@ g, /book/:id, pen_admin
matchingForGFunction: 'keyMatch2',
matchingDomainForGFunction: undefined
};
})();`
})();`,
enforceContext: undefined
},
rbac_with_all_pattern: {
name: 'RBAC with all pattern',
Expand Down Expand Up @@ -233,7 +243,8 @@ g, /book/:id, book_group, *`,
matchingForGFunction: 'keyMatch2',
matchingDomainForGFunction: 'keyMatch2'
};
})();`
})();`,
enforceContext: undefined
},
rbac_with_deny: {
name: 'RBAC with deny-override',
Expand All @@ -260,7 +271,8 @@ p, alice, data2, write, deny
g, alice, data2_admin`,
request: `alice, data1, read
alice, data2, write`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
abac: {
name: 'ABAC',
Expand All @@ -278,7 +290,8 @@ m = r.sub == r.obj.Owner`,
policy: '',
request: `alice, { Owner: 'alice'}
alice, { Owner: 'bob'}`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
abac_with_policy_rule: {
name: 'ABAC with policy rule',
Expand All @@ -296,7 +309,8 @@ 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
customConfig: undefined,
enforceContext: undefined
},
keymatch: {
name: 'RESTful (KeyMatch)',
Expand All @@ -319,7 +333,8 @@ p, bob, /bob_data/*, POST
p, cathy, /cathy_data, (GET)|(POST)`,
request: 'alice, /alice_data/hello, GET',
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
keymatch2: {
name: 'RESTful (KeyMatch2)',
Expand All @@ -338,7 +353,8 @@ m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)`,
p, alice, /alice_data2/:id/using/:resId, GET`,
request: `alice, /alice_data/hello, GET
alice, /alice_data/hello, POST`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
ipmatch: {
name: 'IP match',
Expand All @@ -357,7 +373,8 @@ m = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
p, 10.0.0.0/16, data2, write`,
request: `192.168.2.1, data1, read
10.0.2.3, data2, write`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
},
priority: {
name: 'Priority',
Expand Down Expand Up @@ -388,7 +405,8 @@ p, bob, data2, write, deny
g, bob, data2_allow_group`,
request: `alice, data1, read`,
customConfig: undefined
customConfig: undefined,
enforceContext: undefined
}
};

Expand All @@ -409,5 +427,11 @@ export const defaultCustomConfig = `(function() {
matchingDomainForGFunction: undefined
};
})();`;
export type Example = typeof example;
export type ModelKind = keyof Example;
export const defaultEnforceContext = `{
"r": "r",
"p": "p",
"e": "e",
"m": "m"
}`;

export type ModelKind = string;
29 changes: 26 additions & 3 deletions src/editor/index.tsx
Expand Up @@ -8,9 +8,9 @@ import RunTest from './run-test';
import { ModelKind } from './casbin-mode/example';
import { Settings } from './settings';
import styled from 'styled-components';
// import { useLocalStorage } from './use-local-storage';
import Share, { ShareFormat } from './share';
import Copy from './copy';
import { defaultEnforceContextData, SetupEnforceContext } from './setup-enforce-context';

const Container = styled.div`
display: flex;
Expand All @@ -26,6 +26,7 @@ export const EditorScreen = () => {
const [requestResult, setRequestResult] = useState('');
const [customConfig, setCustomConfig] = useState('');
const [share, setShare] = useState('');
const [enforceContextData, setEnforceContextData] = useState(new Map(defaultEnforceContextData));

function setPolicyPersistent(text: string): void {
set(Persist.POLICY, text);
Expand All @@ -47,6 +48,12 @@ export const EditorScreen = () => {
setRequest(text);
}

function setEnforceContextDataPersistent(map: Map<string, string>): void {
const text = JSON.stringify(Object.fromEntries(map));
set(Persist.ENFORCE_CONTEXT, text);
setEnforceContextData(new Map(map));
}

useEffect(() => {
const hash = window.location.hash.slice(1);
if (hash) {
Expand All @@ -59,6 +66,10 @@ export const EditorScreen = () => {
setModelTextPersistent(sharedContent.model);
setCustomConfigPersistent(sharedContent.customConfig);
setRequestPersistent(sharedContent.request);
setRequestPersistent(sharedContent.request);
if (sharedContent.enforceContext) {
setEnforceContextDataPersistent(new Map(Object.entries(sharedContent.enforceContext)));
}
setRequestResult('');
window.location.hash = ''; // prevent duplicate load
setEcho(<Echo>Shared Content Loaded.</Echo>);
Expand All @@ -74,6 +85,7 @@ export const EditorScreen = () => {
setModelText(get(Persist.MODEL, modelKind));
setRequest(get(Persist.REQUEST, modelKind));
setCustomConfig(get(Persist.CUSTOM_FUNCTION, modelKind));
setEnforceContextData(new Map(Object.entries(JSON.parse(get(Persist.ENFORCE_CONTEXT, modelKind)!))));
}, [modelKind]);

function handleShare(v: JSX.Element | string) {
Expand Down Expand Up @@ -127,7 +139,10 @@ export const EditorScreen = () => {

<FlexRow>
<EditorContainer>
<HeaderTitle>Request</HeaderTitle>
<FlexRow>
<HeaderTitle>Request</HeaderTitle>
<SetupEnforceContext data={enforceContextData} onChange={setEnforceContextDataPersistent} />
</FlexRow>
<RequestEditor text={request} onChange={setRequestPersistent} />
</EditorContainer>
<EditorContainer>
Expand All @@ -144,6 +159,7 @@ export const EditorScreen = () => {
policy={policy}
customConfig={customConfig}
request={request}
enforceContextData={enforceContextData}
onResponse={v => {
if (isValidElement(v)) {
setEcho(v);
Expand All @@ -153,7 +169,14 @@ export const EditorScreen = () => {
}}
/>
{!share ? (
<Share onResponse={v => handleShare(v)} model={modelText} policy={policy} customConfig={customConfig} request={request} />
<Share
onResponse={v => handleShare(v)}
model={modelText}
policy={policy}
customConfig={customConfig}
request={request}
enforceContext={Object.entries(enforceContextData)}
/>
) : (
<Copy
content={share}
Expand Down
11 changes: 7 additions & 4 deletions src/editor/persist.ts
@@ -1,12 +1,13 @@
import { defaultCustomConfig, example, ModelKind } from './casbin-mode/example';
import { defaultCustomConfig, defaultEnforceContext, example, ModelKind } from './casbin-mode/example';

export const DEFAULT_MODEL: ModelKind = 'basic';
export const DEFAULT_MODEL = 'basic';

export enum Persist {
MODEL,
POLICY,
REQUEST,
CUSTOM_FUNCTION
CUSTOM_FUNCTION,
ENFORCE_CONTEXT
}

function getKey(persist: Persist, modelName: string) {
Expand All @@ -22,7 +23,7 @@ export function setSelectedModel(value: string) {
window.localStorage.setItem(Persist[Persist.MODEL], value);
}

export function get(persist: Persist, modelName: ModelKind = DEFAULT_MODEL) {
export function get(persist: Persist, modelName: ModelKind = DEFAULT_MODEL): string {
const data = window.localStorage.getItem(getKey(persist, modelName));

if (data) {
Expand All @@ -39,6 +40,8 @@ export function get(persist: Persist, modelName: ModelKind = DEFAULT_MODEL) {
return m.request;
case Persist.CUSTOM_FUNCTION:
return m.customConfig ? m.customConfig : defaultCustomConfig;
case Persist.ENFORCE_CONTEXT:
return m.enforceContext ? m.enforceContext : defaultEnforceContext;
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/editor/run-test.tsx
@@ -1,13 +1,15 @@
import React from 'react';
import { Button, Echo } from '../ui';
import { DefaultRoleManager, newEnforcer, newModel, StringAdapter, Util } from 'casbin';
import { newEnforceContext } from './setup-enforce-context';

interface RunTestProps {
model: string;
modelKind: string;
policy: string;
customConfig: string;
request: string;
enforceContextData: Map<string, string>;
onResponse: (com: JSX.Element | any[]) => void;
// parseABAC: boolean;
}
Expand Down Expand Up @@ -146,7 +148,9 @@ async function enforcer(props: RunTestProps) {
}

const rvals = parseABACRequest(n);
result.push(await e.enforce(...rvals));
const ctx = newEnforceContext(props.enforceContextData);

result.push(await e.enforce(ctx, ...rvals));
}

const stopTime = performance.now();
Expand Down
64 changes: 64 additions & 0 deletions src/editor/setup-enforce-context.tsx
@@ -0,0 +1,64 @@
import styled from 'styled-components';
import React, { useEffect, useState } from 'react';
import { EnforceContext } from 'casbin';

const EnforceContextInput = styled.input`
width: 20px;
margin: 0 5px;
`;
interface SetupEnforceContextProps {
data: Map<string, string>;
onChange: (data: Map<string, string>) => void;
}

const r = 'r';
const p = 'p';
const e = 'e';
const m = 'm';

export const defaultEnforceContextData = new Map<string, string>([
[r, r],
[p, p],
[e, e],
[m, m]
]);

export const newEnforceContext = (data: Map<string, string>) => {
return new EnforceContext(data.get(r)!, data.get(p)!, data.get(e)!, data.get(m)!);
};

export const SetupEnforceContext = ({ onChange, data }: SetupEnforceContextProps) => {
const [enforceContextData, setEnforceContextData] = useState(new Map(defaultEnforceContextData));
const handleEnforceContextChange = (key: string, value: string) => {
onChange(data.set(key, value));
};

useEffect(() => {
setEnforceContextData(data);
}, [data]);

return (
<>
<EnforceContextInput
value={enforceContextData.get(r)}
placeholder={r}
onChange={event => handleEnforceContextChange(r, event.target.value)}
/>
<EnforceContextInput
value={enforceContextData.get(p)}
placeholder={p}
onChange={event => handleEnforceContextChange(p, event.target.value)}
/>
<EnforceContextInput
value={enforceContextData.get(e)}
placeholder={e}
onChange={event => handleEnforceContextChange(e, event.target.value)}
/>
<EnforceContextInput
value={enforceContextData.get(m)}
placeholder={m}
onChange={event => handleEnforceContextChange(m, event.target.value)}
/>
</>
);
};

0 comments on commit c42450d

Please sign in to comment.