diff --git a/src/editor/casbin-mode/example.ts b/src/editor/casbin-mode/example.ts index 5aca9e3..88e6f93 100644 --- a/src/editor/casbin-mode/example.ts +++ b/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] @@ -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', @@ -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', @@ -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', @@ -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', @@ -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', @@ -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', @@ -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', @@ -192,7 +201,8 @@ g, /book/:id, pen_admin matchingForGFunction: 'keyMatch2', matchingDomainForGFunction: undefined }; -})();` +})();`, + enforceContext: undefined }, rbac_with_all_pattern: { name: 'RBAC with all pattern', @@ -233,7 +243,8 @@ g, /book/:id, book_group, *`, matchingForGFunction: 'keyMatch2', matchingDomainForGFunction: 'keyMatch2' }; -})();` +})();`, + enforceContext: undefined }, rbac_with_deny: { name: 'RBAC with deny-override', @@ -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', @@ -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', @@ -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)', @@ -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)', @@ -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', @@ -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', @@ -388,7 +405,8 @@ p, bob, data2, write, deny g, bob, data2_allow_group`, request: `alice, data1, read`, - customConfig: undefined + customConfig: undefined, + enforceContext: undefined } }; @@ -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; diff --git a/src/editor/index.tsx b/src/editor/index.tsx index 269d327..19b6262 100755 --- a/src/editor/index.tsx +++ b/src/editor/index.tsx @@ -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; @@ -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); @@ -47,6 +48,12 @@ export const EditorScreen = () => { setRequest(text); } + function setEnforceContextDataPersistent(map: Map): 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) { @@ -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(Shared Content Loaded.); @@ -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) { @@ -127,7 +139,10 @@ export const EditorScreen = () => { - Request + + Request + + @@ -144,6 +159,7 @@ export const EditorScreen = () => { policy={policy} customConfig={customConfig} request={request} + enforceContextData={enforceContextData} onResponse={v => { if (isValidElement(v)) { setEcho(v); @@ -153,7 +169,14 @@ export const EditorScreen = () => { }} /> {!share ? ( - handleShare(v)} model={modelText} policy={policy} customConfig={customConfig} request={request} /> + handleShare(v)} + model={modelText} + policy={policy} + customConfig={customConfig} + request={request} + enforceContext={Object.entries(enforceContextData)} + /> ) : ( ; onResponse: (com: JSX.Element | any[]) => void; // parseABAC: boolean; } @@ -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(); diff --git a/src/editor/setup-enforce-context.tsx b/src/editor/setup-enforce-context.tsx new file mode 100644 index 0000000..8b3fa53 --- /dev/null +++ b/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; + onChange: (data: Map) => void; +} + +const r = 'r'; +const p = 'p'; +const e = 'e'; +const m = 'm'; + +export const defaultEnforceContextData = new Map([ + [r, r], + [p, p], + [e, e], + [m, m] +]); + +export const newEnforceContext = (data: Map) => { + 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 ( + <> + handleEnforceContextChange(r, event.target.value)} + /> + handleEnforceContextChange(p, event.target.value)} + /> + handleEnforceContextChange(e, event.target.value)} + /> + handleEnforceContextChange(m, event.target.value)} + /> + + ); +}; diff --git a/src/editor/share.tsx b/src/editor/share.tsx index b75b0f4..2b9831d 100644 --- a/src/editor/share.tsx +++ b/src/editor/share.tsx @@ -10,6 +10,7 @@ export interface ShareFormat { policy: string; customConfig: string; request: string; + enforceContext: object; } async function dpaste(content: string) { @@ -32,7 +33,8 @@ const Share = (props: ShareProps) => { model: props.model, policy: props.policy, customConfig: props.customConfig, - request: props.request + request: props.request, + enforceContext: props.enforceContext }; dpaste(JSON.stringify(shareContent)).then((url: string) => { setSharing(false);