Skip to content

Commit

Permalink
feat: add updatePolicy() (#234)
Browse files Browse the repository at this point in the history
* feat: add updatePolicy()

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* feat: add unittest for updatePolicy()

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* feat: add updateForUpdatePolicy()

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* feat: add updatePolicy()

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* refactor: rename interface

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* docs: fix comment

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* fix: fix adapter error

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* feat: add updatableFileAdapter

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* fix: remove incorrect claim and fix comment

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* fix: fix comment

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* perf: refactor updatePolicy

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* fix: fix index

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* style: prevent conflict

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* fix: remove UpdatableFileAdapter

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>

* fix: fix unittest()
In fact,this makes unittest cannot pass.The fix will refer to issue #235

Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>
  • Loading branch information
Zxilly authored Feb 6, 2021
1 parent da608ae commit a3218f1
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/coreEnforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { compile, compileAsync } from 'expression-eval';

import { DefaultEffector, Effect, Effector } from './effect';
import { FunctionMap, Model, newModel, PolicyOp } from './model';
import { Adapter, FilteredAdapter, Watcher, BatchAdapter } from './persist';
import { Adapter, FilteredAdapter, Watcher, BatchAdapter, UpdatableAdapter } from './persist';
import { DefaultRoleManager, RoleManager } from './rbac';
import { escapeAssertion, generateGFunction, getEvalValue, hasEval, replaceEval, generatorRunSync, generatorRunAsync } from './util';
import { getLogger, logPrint } from './log';
Expand All @@ -33,7 +33,7 @@ export class CoreEnforcer {
protected eft: Effector = new DefaultEffector();
private matcherMap: Map<string, Matcher> = new Map();

protected adapter: FilteredAdapter | Adapter | BatchAdapter;
protected adapter: UpdatableAdapter | FilteredAdapter | Adapter | BatchAdapter;
protected watcher: Watcher | null = null;
protected rm: RoleManager = new DefaultRoleManager(10);

Expand Down
40 changes: 39 additions & 1 deletion src/internalEnforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
// limitations under the License.

import { CoreEnforcer } from './coreEnforcer';
import { BatchAdapter } from './persist/batchAdapter';
import { BatchAdapter } from './persist';
import { UpdatableAdapter } from './persist';
import { PolicyOp } from './model';

/**
Expand Down Expand Up @@ -85,6 +86,43 @@ export class InternalEnforcer extends CoreEnforcer {
return ok;
}

/**
* updatePolicyInternal updates a rule from the current policy.
*/
public async updatePolicyInternal(sec: string, ptype: string, oldRule: string[], newRule: string[]): Promise<boolean> {
if (!this.model.hasPolicy(sec, ptype, oldRule)) {
return false;
}

if (this.autoSave) {
if ('updatePolicy' in this.adapter) {
try {
await this.adapter.updatePolicy(sec, ptype, oldRule, newRule);
} catch (e) {
if (e.message !== 'not implemented') {
throw e;
}
}
} else {
throw new Error('cannot to update policy, the adapter does not implement the UpdatableAdapter');
}
}

if (this.watcher && this.autoNotifyWatcher) {
// In fact I think it should wait for the respond, but they implement add_policy() like this
// error intentionally ignored
this.watcher.update();
}

const ok = this.model.updatePolicy(sec, ptype, oldRule, newRule);
if (sec === 'g' && ok) {
await this.buildIncrementalRoleLinks(PolicyOp.PolicyRemove, ptype, [oldRule]);
await this.buildIncrementalRoleLinks(PolicyOp.PolicyAdd, ptype, [newRule]);
}

return ok;
}

/**
* removePolicyInternal removes a rule from the current policy.
*/
Expand Down
27 changes: 27 additions & 0 deletions src/managementEnforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,33 @@ export class ManagementEnforcer extends InternalEnforcer {
return this.addPoliciesInternal('p', ptype, rules);
}

/**
* updatePolicy updates an authorization rule from the current policy.
* If the rule not exists, the function returns false.
* Otherwise the function returns true by changing it to the new rule.
*
* @return succeeds or not.
* @param oldRule the policy will be remove
* @param newRule the policy will be added
*/
public async updatePolicy(oldRule: string[], newRule: string[]): Promise<boolean> {
return this.updateNamedPolicy('p', oldRule, newRule);
}

/**
* updateNamedPolicy updates an authorization rule from the current named policy.
* If the rule not exists, the function returns false.
* Otherwise the function returns true by changing it to the new rule.
*
* @param ptype the policy type, can be "p", "p2", "p3", ..
* @param oldRule the policy rule will be remove
* @param newRule the policy rule will be added
* @return succeeds or not.
*/
public async updateNamedPolicy(ptype: string, oldRule: string[], newRule: string[]): Promise<boolean> {
return this.updatePolicyInternal('p', ptype, oldRule, newRule);
}

/**
* removePolicy removes an authorization rule from the current policy.
*
Expand Down
17 changes: 17 additions & 0 deletions src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,23 @@ export class Model {
return [true, rules];
}

// updatePolicy updates a policy from the model
public updatePolicy(sec: string, ptype: string, oldRule: string[], newRule: string[]): boolean {
if (this.hasPolicy(sec, ptype, oldRule)) {
const ast = this.model.get(sec)?.get(ptype);
if (!ast) {
return false;
}
// const index = ast.policy.indexOf(oldRule);
const index = ast.policy.findIndex((r) => util.arrayEquals(r, oldRule));
if (index !== -1) {
ast.policy[index] = newRule;
return true;
}
}
return false;
}

// removePolicy removes a policy rule from the model.
public removePolicy(sec: string, key: string, rule: string[]): boolean {
if (this.hasPolicy(sec, key, rule)) {
Expand Down
2 changes: 2 additions & 0 deletions src/persist/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export * from './watcher';
export * from './filteredAdapter';
export * from './defaultFilteredAdapter';
export * from './batchAdapter';
export * from './batchFileAdapter';
export * from './updatableAdapter';
22 changes: 22 additions & 0 deletions src/persist/updatableAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2021 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 { Adapter } from './adapter';

// UpdatableAdapter is the interface for Casbin adapters with update policy functions.
export interface UpdatableAdapter extends Adapter {
// UpdatePolicy updates a policy rule from storage.
// This is part of the Auto-Save feature.
updatePolicy(sec: string, ptype: string, oldRule: string[], newRule: string[]): Promise<void>;
}
22 changes: 22 additions & 0 deletions test/managementAPI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,28 @@ test('addNamedPolicies', async () => {
}
});

test('updatePolicy', async () => {
const a = new FileAdapter('examples/rbac_policy.csv');
e.setAdapter(a);
const p = ['alice', 'data1', 'read'];
const q = ['alice', 'data2', 'read'];
const updated = await e.updatePolicy(p, q);
expect(updated).toBe(true);
expect(await e.hasPolicy(...p)).toBe(false);
expect(await e.hasPolicy(...q)).toBe(true);
});

test('updateNamedPolicy', async () => {
const a = new FileAdapter('examples/rbac_policy.csv');
e.setAdapter(a);
const p = ['alice', 'data1', 'read'];
const q = ['alice', 'data2', 'read'];
const updated = await e.updateNamedPolicy('p', p, q);
expect(updated).toBe(true);
expect(await e.hasPolicy(...p)).toBe(false);
expect(await e.hasPolicy(...q)).toBe(true);
});

test('removePolicy', async () => {
const p = ['alice', 'data1', 'read'];
const removed = await e.removePolicy(...p);
Expand Down

0 comments on commit a3218f1

Please sign in to comment.