Skip to content

Commit

Permalink
feat: implement ability to load subset of a policy
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyasadiyodi93 committed Feb 24, 2021
1 parent bc72f6c commit 7d0c456
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 15 deletions.
4 changes: 4 additions & 0 deletions lib/casbin/JsonEnforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class JsonEnforcer extends CachedEnforcer {
await this.invalidateCache();
}

public async addStrPolicy(subject: string, resource: string, action: string) {
await this.addPolicy(subject, resource, action);
}

public async removeJsonPolicy(
subject: JsonAttributes,
resource: JsonAttributes,
Expand Down
179 changes: 179 additions & 0 deletions lib/casbin/JsonFilteredEnforcer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable max-classes-per-file */
import { createQueryBuilder, In, Like } from 'typeorm';
import { newEnforcerWithClass } from 'casbin';
// eslint-disable-next-line import/no-cycle
import { convertJSONToStringInOrder, JsonEnforcer } from './JsonEnforcer';

type JsonAttributes = Record<string, unknown>;
type OneKey<K extends string> = Record<K, unknown>;

export class JsonFilteredEnforcer extends JsonEnforcer {
private static params: any[];

public static setParams(params: any[]) {
this.params = params;
}

public static async getEnforcer() {
return newEnforcerWithClass(JsonEnforcer, ...JsonFilteredEnforcer.params);
}

public async loadPolicySubset(policyObj: any, enforcer: JsonEnforcer) {
const rawSubjects = await createQueryBuilder()
.select('casbin_rule.v1')
.from('casbin_rule', 'casbin_rule')
.where('casbin_rule.ptype = :type', { type: 'g' })
.andWhere('casbin_rule.v0 like :subject', {
subject: convertJSONToStringInOrder(policyObj.subject)
})
.getRawMany();
const subjects = rawSubjects
.map((rG) => rG.v1)
.concat(convertJSONToStringInOrder(policyObj.subject));

const anyAction = Like('%*%');

await enforcer.loadFilteredPolicy({
where: [
{ ptype: 'p', v0: In(subjects) },
{ ptype: 'g', v0: convertJSONToStringInOrder(policyObj.subject) },
{ ptype: 'g2', v0: convertJSONToStringInOrder(policyObj.resource) },
{ ptype: 'g3', v0: convertJSONToStringInOrder(policyObj.action) },
{ ptype: 'g3', v0: anyAction }
]
});
}

public async enforceJson(
subject: JsonAttributes,
resource: JsonAttributes,
action: JsonAttributes
): Promise<boolean> {
// intantiate new enforcer

const enforcer = await JsonFilteredEnforcer.getEnforcer();

// load filtered policy
await this.loadPolicySubset({ subject, resource, action }, enforcer);
// enforceJson

const hasAccess = await enforcer.enforce(
convertJSONToStringInOrder(subject),
convertJSONToStringInOrder(resource),
convertJSONToStringInOrder(action)
);

return hasAccess;
}

public async addJsonPolicy(
subject: JsonAttributes,
resource: JsonAttributes,
action: JsonAttributes
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.addPolicy(
convertJSONToStringInOrder(subject),
convertJSONToStringInOrder(resource),
convertJSONToStringInOrder(action)
);
}

public async addStrPolicy(subject: string, resource: string, action: string) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.addPolicy(subject, resource, action);
}

public async removeJsonPolicy(
subject: JsonAttributes,
resource: JsonAttributes,
action: JsonAttributes
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.removePolicy(
convertJSONToStringInOrder(subject),
convertJSONToStringInOrder(resource),
convertJSONToStringInOrder(action)
);
}

public async addSubjectGroupingJsonPolicy<T extends string>(
subject: OneKey<T>,
jsonAttributes: JsonAttributes
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.addNamedGroupingPolicy(
'g',
convertJSONToStringInOrder(subject),
convertJSONToStringInOrder(jsonAttributes),
'subject'
);
}

public async removeSubjectGroupingJsonPolicy<T extends string>(
subject: OneKey<T>,
jsonAttributes: JsonAttributes
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.removeNamedGroupingPolicy(
'g',
convertJSONToStringInOrder(subject),
convertJSONToStringInOrder(jsonAttributes),
'subject'
);
}

public async addResourceGroupingJsonPolicy<T extends string>(
resource: OneKey<T>,
jsonAttributes: JsonAttributes
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.addNamedGroupingPolicy(
'g2',
convertJSONToStringInOrder(resource),
convertJSONToStringInOrder(jsonAttributes),
'resource'
);
}

// ? Note: this will remove all policies by resource keys and then insert the new one
public async upsertResourceGroupingJsonPolicy<T extends string>(
resource: OneKey<T>,
jsonAttributes: JsonAttributes
) {
await this.removeAllResourceGroupingJsonPolicy(resource);
await this.addResourceGroupingJsonPolicy(resource, jsonAttributes);
}

public async removeAllResourceGroupingJsonPolicy<T extends string>(
resource: OneKey<T>
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.removeFilteredNamedGroupingPolicy(
'g2',
0,
convertJSONToStringInOrder(resource)
);
}

public async addActionGroupingJsonPolicy<T extends string>(
action: OneKey<T>,
jsonAttributes: JsonAttributes
) {
const enforcer = await JsonFilteredEnforcer.getEnforcer();
await enforcer.addNamedGroupingPolicy(
'g3',
convertJSONToStringInOrder(action),
convertJSONToStringInOrder(jsonAttributes),
'action'
);
}
}

export async function newJsonFilteredEnforcer(
...params: any[]
): Promise<JsonEnforcer> {
JsonFilteredEnforcer.setParams(params);
return newEnforcerWithClass(JsonFilteredEnforcer, ...params);
}
54 changes: 41 additions & 13 deletions lib/casbin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,63 @@
import TypeORMAdapter from 'typeorm-adapter';
import * as CasbinPgWatcher from 'casbin-pg-watcher';
import { join } from 'path';
import { newJsonEnforcer, JsonEnforcer } from './JsonEnforcer';
import { JsonEnforcer, newJsonEnforcer } from './JsonEnforcer';
import {
newJsonFilteredEnforcer,
JsonFilteredEnforcer
} from './JsonFilteredEnforcer';

const { newWatcher } = CasbinPgWatcher;

class CasbinSingleton {
// eslint-disable-next-line no-useless-constructor
private constructor() {}

public static enforcer: null | JsonEnforcer = null;
public static enforcer: null | JsonFilteredEnforcer | JsonEnforcer = null;

public static filtered = true;

private static async initJsonEnforcer(
modelPath: any,
policyAdapter: any,
dbConnectionUrl: string
) {
const policyWatcher = await newWatcher({
connectionString: dbConnectionUrl
});

this.enforcer = await newJsonEnforcer(modelPath, policyAdapter);

this.enforcer.setWatcher(policyWatcher);
this.enforcer.enableAutoSave(true);
this.enforcer.enableLog(false);

// Load the policy from DB.
await this.enforcer.loadPolicy();
}

private static async initJsonFilteredEnforcer(
modelPath: any,
policyAdapter: any
) {
this.enforcer = await newJsonFilteredEnforcer(modelPath, policyAdapter);
this.enforcer.enableAutoSave(true);
this.enforcer.enableLog(false);
}

public static async create(dbConnectionUrl: string) {
if (!this.enforcer) {
const policyAdapter = await TypeORMAdapter.newAdapter({
type: 'postgres',
url: dbConnectionUrl
});

const policyWatcher = await newWatcher({
connectionString: dbConnectionUrl
});
const modelPath = join(__dirname, 'model.conf');

this.enforcer = await newJsonEnforcer(modelPath, policyAdapter);
this.enforcer.setWatcher(policyWatcher);
this.enforcer.enableAutoSave(true);
this.enforcer.enableLog(false);

// Load the policy from DB.
await this.enforcer.loadPolicy();
if (CasbinSingleton.filtered) {
await this.initJsonFilteredEnforcer(modelPath, policyAdapter);
} else {
await this.initJsonEnforcer(modelPath, policyAdapter, dbConnectionUrl);
}
}

return this.enforcer;
Expand Down
1 change: 0 additions & 1 deletion test/app/group/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,6 @@ lab.experiment('Group::resource', () => {
userPolicies: []
}
];
debugger;
const sortedResult = parseResults(result);
const sortedExpectedResult = parseResults(expectedResult);
Code.expect(sortedResult).to.equal(<any>sortedExpectedResult);
Expand Down
2 changes: 1 addition & 1 deletion test/lib/casbin/sample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const setupPolicies = async () => {
{ role: 'resource.viewer' }
);

await CasbinSingleton.enforcer.addPolicy(
await CasbinSingleton.enforcer.addStrPolicy(
JSON.stringify({ team: 'de' }),
'*',
JSON.stringify({ role: 'super.admin' })
Expand Down

0 comments on commit 7d0c456

Please sign in to comment.