Skip to content

Commit

Permalink
feat: implementation cachedEnforcer
Browse files Browse the repository at this point in the history
  • Loading branch information
nodece committed Jan 21, 2020
1 parent ab93a92 commit 0ace1a6
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 19 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "casbin",
"version": "3.0.9",
"version": "0.0.0-development",
"description": "An authorization library that supports access control models like ACL, RBAC, ABAC in Node.JS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
66 changes: 66 additions & 0 deletions src/cachedEnforcer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Enforcer, newEnforcerWithClass } from './enforcer';

// CachedEnforcer wraps Enforcer and provides decision cache
export class CachedEnforcer extends Enforcer {
private enableCache = true;
private m = new Map<string, boolean>();

// invalidateCache deletes all the existing cached decisions.
public invalidateCache(): void {
this.m = new Map<string, boolean>();
}

// setEnableCache determines whether to enable cache on e nforce(). When enableCache is enabled, cached result (true | false) will be returned for previous decisions.
public setEnableCache(enableCache: boolean): void {
this.enableCache = enableCache;
}

private static canCache(...rvals: any[]): boolean {
return rvals.every(n => typeof n === 'string');
}

private static getCacheKey(...rvals: string[]): string {
return rvals.join('$$');
}

private getCache(key: string): boolean | undefined {
return this.m.get(key);
}

private setCache(key: string, value: boolean): void {
this.m.set(key, value);
}

// enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
// if rvals is not string , ingore the cache
public async enforce(...rvals: any[]): Promise<boolean> {
if (!this.enableCache) {
return super.enforce(...rvals);
}

let key = '';
const cache = CachedEnforcer.canCache(...rvals);

if (cache) {
key = CachedEnforcer.getCacheKey(...rvals);
const res = this.getCache(key);

if (res != undefined) {
return res;
}
}

const res = await super.enforce(...rvals);

if (cache) {
this.setCache(key, res);
}

return res;
}
}

// newCachedEnforcer creates a cached enforcer via file or DB.
export async function newCachedEnforcer(...params: any[]): Promise<CachedEnforcer> {
return newEnforcerWithClass(CachedEnforcer, ...params);
}
40 changes: 22 additions & 18 deletions src/enforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,24 +317,8 @@ export class Enforcer extends ManagementEnforcer {
}
}

/**
* newEnforcer creates an enforcer via file or DB.
*
* File:
* ```js
* const e = new Enforcer('path/to/basic_model.conf', 'path/to/basic_policy.csv');
* ```
*
* MySQL DB:
* ```js
* const a = new MySQLAdapter('mysql', 'mysql_username:mysql_password@tcp(127.0.0.1:3306)/');
* const e = new Enforcer('path/to/basic_model.conf', a);
* ```
*
* @param params
*/
export async function newEnforcer(...params: any[]): Promise<Enforcer> {
const e = new Enforcer();
export async function newEnforcerWithClass<T extends Enforcer>(enforcer: new () => T, ...params: any[]): Promise<T> {
const e = new enforcer();

let parsedParamLen = 0;
if (params.length >= 1) {
Expand Down Expand Up @@ -373,3 +357,23 @@ export async function newEnforcer(...params: any[]): Promise<Enforcer> {

return e;
}

/**
* newEnforcer creates an enforcer via file or DB.
*
* File:
* ```js
* const e = new Enforcer('path/to/basic_model.conf', 'path/to/basic_policy.csv');
* ```
*
* MySQL DB:
* ```js
* const a = new MySQLAdapter('mysql', 'mysql_username:mysql_password@tcp(127.0.0.1:3306)/');
* const e = new Enforcer('path/to/basic_model.conf', a);
* ```
*
* @param params
*/
export async function newEnforcer(...params: any[]): Promise<Enforcer> {
return newEnforcerWithClass(Enforcer, ...params);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as Util from './util';

export * from './config';
export * from './enforcer';
export * from './cachedEnforcer';
export * from './effect';
export * from './model';
export * from './persist';
Expand Down
25 changes: 25 additions & 0 deletions test/cachedEnforcer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2020 The Casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Enforcer, newCachedEnforcer } from '../src';

async function testEnforce(e: Enforcer, sub: string, obj: string, act: string, res: boolean): Promise<void> {
await expect(e.enforce(sub, obj, act)).resolves.toBe(res);
}

test('TestRBACModel', async () => {
const e = await newCachedEnforcer('examples/rbac_model.conf', 'examples/rbac_policy.csv');

await testEnforce(e, 'alice', 'data1', 'read', true);
});

0 comments on commit 0ace1a6

Please sign in to comment.