Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
rules: {
'@typescript-eslint/no-var-requires': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-unused-vars': 0
'@typescript-eslint/no-unused-vars': 0,
'no-useless-catch': 0
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "casbin.js",
"version": "0.0.2",
"version": "0.0.3",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"repository": "https://github.com/casbin/casbin.js.git",
Expand Down
21 changes: 16 additions & 5 deletions src/Authorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import axios from 'axios';
import Cookies from 'js-cookie';
import Permission from './Permission';
import { StringKV } from './types';
import * as Cache from './Cache'

interface BaseResponse {
message: string;
Expand All @@ -16,23 +17,28 @@ export class Authorizer {
private user : string | undefined;
private permission = new Permission();
private cookieKey : string | undefined = undefined;
private cacheExpiredTime = 60; // Seconds

/**
*
* @param mode "auto", "cookies" or "manual"
* "auto": Specify the casbin server endpoint, and Casbin.js will load permission from it when the identity changes
* "cookies": Casbin.js load the permission data from the cookie "casbin_permission" or the specified cookie key.
* "manual": Load the permission mannually with "setPermission"
* @param args.endpoint Casbin service endpoint, required when mode == "auto"
* @param args.endpoint Casbin service endpoint, REQUIRED when mode == "auto"
* @param args.cacheExpiredTime The expired time of local cache, Unit: seconds, Default: 60s, activated when mode == "auto"
* @param args.cookieKey The cookie key when loading permission, activated when mode == "cookies"
*/
constructor(mode: Mode = "manual", args: {endpoint?: string, cookieKey?: string} = {}) {
constructor(mode: Mode = "manual", args: {endpoint?: string, cookieKey?: string, cacheExpiredTime?: number} = {}) {
if (mode == 'auto') {
if (!args.endpoint) {
throw new Error("Specify the endpoint when initializing casbin.js with mode == 'auto'");
} else {
this.mode = mode;
this.endpoint = args.endpoint;
if (args.cacheExpiredTime !== null && args.cacheExpiredTime !== undefined) {
this.cacheExpiredTime = args.cacheExpiredTime;
}
}
} else if (mode == 'cookies') {
this.mode = mode;
Expand All @@ -53,7 +59,7 @@ export class Authorizer {
* Get the permission.
*/
public getPermission() : StringKV {
return this.permission.getPermissionJson();
return this.permission.getPermissionJsonObject();
}

public setPermission(permission : Record<string, unknown> | string) : void{
Expand All @@ -67,7 +73,6 @@ export class Authorizer {
if (this.endpoint !== undefined && this.endpoint !== null) {
const resp = await axios.get<BaseResponse>(`${this.endpoint}?casbin_subject=${this.user}`);
this.permission.load(resp.data.data);
console.log("syncUserPermission is called")
}
}

Expand All @@ -79,7 +84,13 @@ export class Authorizer {
// Sync with the server and fetch the latest permission of the new user
if (this.mode == 'auto' && user != this.user) {
this.user = user;
await this.syncUserPermission()
const permStr = Cache.loadFromLocalStorage(user);
if (permStr === null) {
await this.syncUserPermission();
Cache.saveToLocalStorage(user, this.permission.getPermissionString(), this.cacheExpiredTime);
} else {
this.permission.load(permStr);
}
}
}

Expand Down
53 changes: 53 additions & 0 deletions src/Cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const isLocalStorageAvailable: boolean = (() => {
try {
const key = "fUjXn2r59"; // A random key
const value = "test";
localStorage.setItem(key, value);
const gotValue = localStorage.getItem(key);
localStorage.removeItem(key);
return true;
} catch (e) {
return false;
}
})();

export function saveToLocalStorage(key: string, value: string, expired: number): number{
if (!isLocalStorageAvailable) {
return -1;
}
const savedItem = {
value: value,
expired: Date.now() + 1000 * expired
};
console.log(savedItem);
try {
localStorage.setItem(`casbinjs_${key}`, JSON.stringify(savedItem));
} catch (e) {
throw(e)
// TODO: Process the quotaExceededError
}
return 0;
}

/***
* return: a string.
* If ret == null, it means there is no such user permission.
*/
export function loadFromLocalStorage(key: string): string | null{
if (!isLocalStorageAvailable) {
return null;
}
const itemStr = localStorage.getItem(`casbinjs_${key}`);
// No cache
if (itemStr === null) {
return null;
}
const item = JSON.parse(itemStr);

if (Date.now() > item["expired"]){
localStorage.removeItem(`casbinjs_${key}`);
return null;
} else {
return item['value'];
}
}
5 changes: 2 additions & 3 deletions src/Permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default class Permission {
this.actObjData = new Map<string, Array<string>>();
this.objActData = new Map<string, Array<string>>();
}


public load(permission : string | Record<string, unknown>) : void {
let p : StringKV;
Expand Down Expand Up @@ -39,7 +38,7 @@ export default class Permission {

}

public getPermissionJson() : StringKV {
public getPermissionJsonObject() : StringKV {
const obj : StringKV = {};
this.actObjData.forEach((value, key) => (obj[key] = value));
return obj;
Expand All @@ -49,7 +48,7 @@ export default class Permission {
Parse the permission into JSON string
*/
public getPermissionString() : string {
return JSON.stringify(this.getPermissionJson());
return JSON.stringify(this.getPermissionJsonObject());
}

public getTargetsFromAction(action: string) : Array<string> {
Expand Down
11 changes: 11 additions & 0 deletions src/__test__/cache.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as Cache from '../Cache';
import * as utils from '../utils';

test("Cache unit test", async () => {
Cache.saveToLocalStorage("test", "test", 1);
expect(Cache.loadFromLocalStorage("test")).toBe("test");
expect(localStorage.getItem("casbinjs_test")).not.toBeNull();
await utils.sleep(2000);
expect(Cache.loadFromLocalStorage("test")).toBeNull();
expect(localStorage.getItem("casbinjs_test")).toBeNull();
})
2 changes: 1 addition & 1 deletion src/__test__/permission.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('Permission unit test', () => {
test('Load permission from JSON object', () => {
const s = JSON.stringify(policyExample);
permission.load(policyExample);
const t = permission.getPermissionJson();
const t = permission.getPermissionJsonObject();
expect(t).toMatchObject(policyExample);
})

Expand Down
3 changes: 3 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": ["es6"], /* Specify library files to be included in the compilation. */
"lib": ["es6", "DOM"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
Expand Down