Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: multiple section types #375

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/multiple_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
p2, alice, data1, read
p2, bob, data2, write
50 changes: 41 additions & 9 deletions src/coreEnforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { DefaultEffector, Effect, Effector } from './effect';
import { FunctionMap, Model, newModel, PolicyOp } from './model';
import { Adapter, FilteredAdapter, Watcher, BatchAdapter, UpdatableAdapter, WatcherEx } from './persist';
import { DefaultRoleManager, RoleManager } from './rbac';
import { EnforceContext } from './enforceContext';

import {
escapeAssertion,
generateGFunction,
Expand Down Expand Up @@ -46,6 +48,7 @@ export class CoreEnforcer {
protected fm: FunctionMap = FunctionMap.loadFunctionMap();
protected eft: Effector = new DefaultEffector();
private matcherMap: Map<string, Matcher> = new Map();
private defaultEnforceContext: EnforceContext = new EnforceContext('r', 'p', 'e', 'm');

protected adapter: UpdatableAdapter | FilteredAdapter | Adapter | BatchAdapter;
protected watcher: Watcher | null = null;
Expand Down Expand Up @@ -381,7 +384,12 @@ export class CoreEnforcer {
}
}

private *privateEnforce(asyncCompile = true, explain = false, ...rvals: any[]): EnforceResult {
private *privateEnforce(
asyncCompile = true,
explain = false,
enforceContext: EnforceContext = new EnforceContext('r', 'p', 'e', 'm'),
...rvals: any[]
): EnforceResult {
if (!this.enabled) {
return true;
}
Expand All @@ -400,23 +408,23 @@ export class CoreEnforcer {
functions[key] = asyncCompile ? generateGFunction(rm) : generateSyncedGFunction(rm);
});

const expString = this.model.model.get('m')?.get('m')?.value;
const expString = this.model.model.get('m')?.get(enforceContext.mType)?.value;
if (!expString) {
throw new Error('Unable to find matchers in model');
}

const effectExpr = this.model.model.get('e')?.get('e')?.value;
const effectExpr = this.model.model.get('e')?.get(enforceContext.eType)?.value;
if (!effectExpr) {
throw new Error('Unable to find policy_effect in model');
}

const HasEval: boolean = hasEval(expString);
let expression: Matcher | undefined = undefined;

const p = this.model.model.get('p')?.get('p');
const p = this.model.model.get('p')?.get(enforceContext.pType);
const policyLen = p?.policy?.length;

const rTokens = this.model.model.get('r')?.get('r')?.tokens;
const rTokens = this.model.model.get('r')?.get(enforceContext.rType)?.tokens;
const rTokensLen = rTokens?.length;

const effectStream = this.eft.newStream(effectExpr);
Expand Down Expand Up @@ -564,7 +572,13 @@ export class CoreEnforcer {
* @return whether to allow the request.
*/
public enforceSync(...rvals: any[]): boolean {
return generatorRunSync(this.privateEnforce(false, false, ...rvals));
if (rvals.length) {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunSync(this.privateEnforce(false, false, enforceContext, ...rvals));
}
}
return generatorRunSync(this.privateEnforce(false, false, this.defaultEnforceContext, ...rvals));
}

/**
Expand All @@ -578,7 +592,13 @@ export class CoreEnforcer {
* @return whether to allow the request and the reason rule.
*/
public enforceExSync(...rvals: any[]): [boolean, string[]] {
return generatorRunSync(this.privateEnforce(false, true, ...rvals));
if (rvals.length) {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunSync(this.privateEnforce(false, true, enforceContext, ...rvals));
}
}
return generatorRunSync(this.privateEnforce(false, true, this.defaultEnforceContext, ...rvals));
}

/**
Expand All @@ -597,7 +617,13 @@ export class CoreEnforcer {
* @return whether to allow the request.
*/
public async enforce(...rvals: any[]): Promise<boolean> {
return generatorRunAsync(this.privateEnforce(true, false, ...rvals));
if (rvals.length) {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunAsync(this.privateEnforce(true, false, enforceContext, ...rvals));
}
}
return generatorRunAsync(this.privateEnforce(true, false, this.defaultEnforceContext, ...rvals));
}

/**
Expand All @@ -609,7 +635,13 @@ export class CoreEnforcer {
* @return whether to allow the request and the reason rule.
*/
public async enforceEx(...rvals: any[]): Promise<[boolean, string[]]> {
return generatorRunAsync(this.privateEnforce(true, true, ...rvals));
if (rvals.length) {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunAsync(this.privateEnforce(true, true, enforceContext, ...rvals));
}
}
return generatorRunAsync(this.privateEnforce(true, true, this.defaultEnforceContext, ...rvals));
}

/**
Expand Down
20 changes: 20 additions & 0 deletions src/enforceContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { newEnforcer } from './enforcer';

export class EnforceContext {
public pType: string;
public rType: string;
public eType: string;
public mType: string;

constructor(rType: string, pType: string, eType: string, mType: string) {
this.pType = pType;
this.eType = eType;
this.mType = mType;
this.rType = rType;
}
}
export class NewEnforceContext {
constructor(index: string) {
return new EnforceContext('r' + index, 'p' + index, 'e' + index, 'm' + index);
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ export * from './model';
export * from './persist';
export * from './rbac';
export * from './log';
export { EnforceContext } from './enforceContext';
export * from './frontend';
export { Util };
5 changes: 4 additions & 1 deletion src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ export class Model {
/**
* constructor is the constructor for Model.
*/
constructor() {
constructor(text?: string) {
this.model = new Map<string, Map<string, Assertion>>();
if (text) {
this.loadModelFromText(text);
}
}

private loadAssertion(cfg: ConfigInterface, sec: string, key: string): boolean {
Expand Down
64 changes: 64 additions & 0 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,68 @@ function deepCopy(obj: Array<any> | any): any {
return newObj;
}

function policyArrayToString(policy: string[]): string {
return policy
.map((n) => {
return `"${(n === null ? '' : n.toString()).replace(/"/g, '""')}"`;
})
.join(',');
}

function policyStringToArray(policy: string): string[][] {
const endCommaRe = /,$/;
const quotaWrapperRe = /^".*"$/;

const lines: string[] = policy.split(/\r?\n/);
const arrays: string[][] = [];

for (let line of lines) {
const commentLabel = line.indexOf('#');
if (commentLabel !== -1) {
line = line.substr(0, commentLabel);
}

line = line.trim();

if (endCommaRe.test(line)) {
throw new Error('The csv standard does not allow a comma at the end of a sentence');
}

const slices = line.split(',');
let tokens: string[] = [];

for (let slice of slices) {
slice = slice.trim();

// Remove parcel quotes
if (quotaWrapperRe.test(slice)) {
slice = slice.substr(1, slice.length - 2);
}

if (slice.includes('""')) {
// "" Escape processing
for (let i = 0; i < slice.length; ) {
if (slice[i] === '"') {
if (slice[i + 1] !== '"') {
throw new Error(`Unescaped " at ${line}`);
}
i += 2;
}
i += 1;
}

slice = slice.replace(/""/g, '"');
}

tokens.push(slice);
}

arrays.push(deepCopy(tokens));
tokens = [];
}
return arrays;
}

function customIn(a: number | string, b: number | string): number {
if ((b as any) instanceof Array) {
return (((b as any) as Array<any>).includes(a) as unknown) as number;
Expand Down Expand Up @@ -218,6 +280,8 @@ export {
generatorRunSync,
generatorRunAsync,
deepCopy,
policyArrayToString,
policyStringToArray,
customIn,
bracketCompatible,
};
84 changes: 74 additions & 10 deletions test/enforcer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import { readFileSync } from 'fs';

import { newModel, newEnforcer, Enforcer, FileAdapter, StringAdapter, Util } from '../src';
import { NewEnforceContext, EnforceContext } from '../src/enforceContext';

async function testEnforce(e: Enforcer, sub: any, obj: string, act: string, res: boolean): Promise<void> {
await expect(e.enforce(sub, obj, act)).resolves.toBe(res);
Expand Down Expand Up @@ -523,26 +524,18 @@ describe('Unimplemented File Adapter methods', () => {

describe('Unimplemented String Adapter methods', () => {
let e = {} as Enforcer;
let a = {} as StringAdapter;
const a = {} as StringAdapter;

beforeEach(async () => {
const policy = readFileSync('examples/basic_policy.csv').toString();
a = new StringAdapter(policy);
const a = new StringAdapter(policy);
e = await newEnforcer('examples/basic_model.conf', a);
});

test('savePolicy', async () => {
await expect(a.savePolicy(e.getModel())).rejects.toThrow('not implemented');
});

test('addPolicy', async () => {
await expect(a.addPolicy('', '', [''])).rejects.toThrow('not implemented');
});

test('removePolicy', async () => {
await expect(a.removePolicy('', '', [''])).rejects.toThrow('not implemented');
});

test('removeFilteredPolicy', async () => {
await expect(a.removeFilteredPolicy('', '', 0, '')).rejects.toThrow('not implemented');
});
Expand Down Expand Up @@ -698,6 +691,77 @@ test('TestEnforceExWithPriorityModel', async () => {
testEnforceEx(e, 'alice', 'data2', 'read', [false, []]);
});

test('TestEnforce Multiple policies config', async () => {
const m = newModel();
m.addDef('r', 'r2', 'sub, obj, act');
m.addDef('p', 'p2', 'sub, obj, act');
m.addDef('g', 'g', '_, _');
m.addDef('e', 'e2', 'some(where (p.eft == allow))');
m.addDef('m', 'm2', 'g(r2.sub, p2.sub) && r2.obj == p2.obj && r2.act == p2.act');
const policy = readFileSync('examples/multiple_policy.csv').toString();
const a = new StringAdapter(policy);

const e = await newEnforcer(m, a);

//const e = await getEnforcerWithPath(m);
const enforceContext = new EnforceContext('r2', 'p2', 'e2', 'm2');
await expect(e.enforce(enforceContext, 'alice', 'data1', 'read')).resolves.toStrictEqual(true);
await expect(e.enforce(enforceContext, 'bob', 'data2', 'write')).resolves.toStrictEqual(true);
});

test('new EnforceContext config', async () => {
const m = newModel();
m.addDef('r', 'r2', 'sub, obj, act');
m.addDef('p', 'p2', 'sub, obj, act');
m.addDef('g', 'g', '_, _');
m.addDef('e', 'e2', 'some(where (p.eft == allow))');
m.addDef('m', 'm2', 'g(r2.sub, p2.sub) && r2.obj == p2.obj && r2.act == p2.act');
const policy = readFileSync('examples/multiple_policy.csv').toString();
const a = new StringAdapter(policy);

const e = await newEnforcer(m, a);

//const e = await getEnforcerWithPath(m);
const enforceContext = new NewEnforceContext('2');
await expect(e.enforce(enforceContext, 'alice', 'data1', 'read')).resolves.toStrictEqual(true);
await expect(e.enforce(enforceContext, 'bob', 'data2', 'write')).resolves.toStrictEqual(true);
});

test('TestEnforceEX Multiple policies config', async () => {
const m = newModel();
m.addDef('r', 'r2', 'sub, obj, act');
m.addDef('p', 'p2', 'sub, obj, act');
m.addDef('g', 'g', '_, _');
m.addDef('e', 'e2', 'some(where (p.eft == allow))');
m.addDef('m', 'm2', 'g(r2.sub, p2.sub) && r2.obj == p2.obj && r2.act == p2.act');
const policy = readFileSync('examples/multiple_policy.csv').toString();
const a = new StringAdapter(policy);

const e = await newEnforcer(m, a);

//const e = await getEnforcerWithPath(m);
const enforceContext = new EnforceContext('r2', 'p2', 'e2', 'm2');
await expect(e.enforceEx(enforceContext, 'alice', 'data1', 'read')).resolves.toStrictEqual([true, ['alice', 'data1', 'read']]);
await expect(e.enforceEx(enforceContext, 'bob', 'data2', 'write')).resolves.toStrictEqual([true, ['bob', 'data2', 'write']]);
});

test('new EnforceContextEX config', async () => {
const m = newModel();
m.addDef('r', 'r2', 'sub, obj, act');
m.addDef('p', 'p2', 'sub, obj, act');
m.addDef('g', 'g', '_, _');
m.addDef('e', 'e2', 'some(where (p.eft == allow))');
m.addDef('m', 'm2', 'g(r2.sub, p2.sub) && r2.obj == p2.obj && r2.act == p2.act');
const policy = readFileSync('examples/multiple_policy.csv').toString();
const a = new StringAdapter(policy);
const e = await newEnforcer(m, a);

//const e = await getEnforcerWithPath(m);
const enforceContext = new NewEnforceContext('2');
await expect(e.enforceEx(enforceContext, 'alice', 'data1', 'read')).resolves.toStrictEqual([true, ['alice', 'data1', 'read']]);
await expect(e.enforceEx(enforceContext, 'bob', 'data2', 'write')).resolves.toStrictEqual([true, ['bob', 'data2', 'write']]);
});

test('TestABACAtrrModel', async () => {
const e = await newEnforcer('examples/abac_attr_model.conf', 'examples/abac_attr_policy.csv');
expect(
Expand Down
Loading