From 612a9c9d3b8fd8e6ed47182dd5999cee77644c04 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 18 Jul 2023 14:30:44 +0300 Subject: [PATCH] fix: Fix failing typeorm-adapter with ACL model (#61) Signed-off-by: Oleksandr Andriienko --- .gitignore | 1 + examples/acl_model.conf | 11 ++ examples/acl_policy.csv | 2 + package.json | 2 +- src/adapter.ts | 12 +- test/adapter-with-acl-model.test.ts | 131 ++++++++++++++++++ ...est.ts => adapter-with-rbac-model.test.ts} | 0 7 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 examples/acl_model.conf create mode 100644 examples/acl_policy.csv create mode 100644 test/adapter-with-acl-model.test.ts rename test/{adapter.test.ts => adapter-with-rbac-model.test.ts} (100%) diff --git a/.gitignore b/.gitignore index 1588829..2096cdb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules lib yarn-error.log package-lock.json +coverage diff --git a/examples/acl_model.conf b/examples/acl_model.conf new file mode 100644 index 0000000..dc6da81 --- /dev/null +++ b/examples/acl_model.conf @@ -0,0 +1,11 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = r.sub == p.sub && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/acl_policy.csv b/examples/acl_policy.csv new file mode 100644 index 0000000..0ee8a38 --- /dev/null +++ b/examples/acl_policy.csv @@ -0,0 +1,2 @@ +p, alice, data1, read +p, bob, data2, write diff --git a/package.json b/package.json index c7ca1bd..854a81c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "precommit": "lint-staged", "prepublish": "yarn run lint && yarn build", "build": "rimraf lib && tsc", - "coverage": "jest --coverage", + "coverage": "jest --coverage --runInBand", "lint": "tslint \"src/**/*.ts\"", "fix": "tslint \"src/**/*.ts\" --fix", "test": "jest --runInBand", diff --git a/src/adapter.ts b/src/adapter.ts index bf71e43..a85f3e3 100644 --- a/src/adapter.ts +++ b/src/adapter.ts @@ -195,11 +195,13 @@ export default class TypeORMAdapter implements FilteredAdapter { } astMap = model.model.get('g'); - // @ts-ignore - for (const [ptype, ast] of astMap) { - for (const rule of ast.policy) { - const line = this.savePolicyLine(ptype, rule); - lines.push(line); + if (astMap) { + // @ts-ignore + for (const [ptype, ast] of astMap) { + for (const rule of ast.policy) { + const line = this.savePolicyLine(ptype, rule); + lines.push(line); + } } } diff --git a/test/adapter-with-acl-model.test.ts b/test/adapter-with-acl-model.test.ts new file mode 100644 index 0000000..7529e80 --- /dev/null +++ b/test/adapter-with-acl-model.test.ts @@ -0,0 +1,131 @@ +// Copyright 2018 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, Util } from 'casbin'; +import TypeORMAdapter from '../src/index'; +import { connectionConfig } from './config'; + +test( + 'TestAdapter', + async () => { + const a = await TypeORMAdapter.newAdapter(connectionConfig); + try { + // Because the DB is empty at first, + // so we need to load the policy from the file adapter (.CSV) first. + let e = new Enforcer(); + + await e.initWithFile( + 'examples/acl_model.conf', + 'examples/acl_policy.csv', + ); + + // This is a trick to save the current policy to the DB. + // We can't call e.savePolicy() because the adapter in the enforcer is still the file adapter. + // The current policy means the policy in the Node-Casbin enforcer (aka in memory). + await a.savePolicy(e.getModel()); + + // Clear the current policy. + e.clearPolicy(); + expect(await e.getPolicy()).toEqual([]); + + // Load the policy from DB. + await a.loadPolicy(e.getModel()); + expect(await e.getPolicy()).toEqual([ + ['alice', 'data1', 'read'], + ['bob', 'data2', 'write'], + ]); + + // Note: you don't need to look at the above code + // if you already have a working DB with policy inside. + + // Now the DB has policy, so we can provide a normal use case. + // Create an adapter and an enforcer. + // newEnforcer() will load the policy automatically. + e = new Enforcer(); + await e.initWithAdapter('examples/rbac_model.conf', a); + expect(await e.getPolicy()).toEqual([ + ['alice', 'data1', 'read'], + ['bob', 'data2', 'write'], + ]); + + // load filtered policies + e.clearPolicy(); + await a.loadFilteredPolicy(e.getModel(), { ptype: 'p', v0: 'alice' }); + expect(await e.getFilteredNamedPolicy('p', 0, 'alice')).toEqual([ + ['alice', 'data1', 'read'], + ]); + + // Add policy to DB + await a.addPolicy('', 'p', ['role', 'res', 'action']); + e = new Enforcer(); + await e.initWithAdapter('examples/rbac_model.conf', a); + expect(await e.getPolicy()).toEqual([ + ['alice', 'data1', 'read'], + ['bob', 'data2', 'write'], + ['role', 'res', 'action'], + ]); + + await a.addPolicies('', 'p', [ + ['role1', 'res1', 'action1'], + ['role2', 'res2', 'action2'], + ['role3', 'res3', 'action3'], + ['role4', 'res4', 'action4'], + ['role5', 'res5', 'action5'], + ]); + e = new Enforcer(); + await e.initWithAdapter('examples/rbac_model.conf', a); + expect(await e.getPolicy()).toEqual([ + ['alice', 'data1', 'read'], + ['bob', 'data2', 'write'], + ['role', 'res', 'action'], + ['role1', 'res1', 'action1'], + ['role2', 'res2', 'action2'], + ['role3', 'res3', 'action3'], + ['role4', 'res4', 'action4'], + ['role5', 'res5', 'action5'], + ]); + + // Remove policy from DB + await a.removePolicy('', 'p', ['role', 'res', 'action']); + e = new Enforcer(); + await e.initWithAdapter('examples/rbac_model.conf', a); + expect(await e.getPolicy()).toEqual([ + ['alice', 'data1', 'read'], + ['bob', 'data2', 'write'], + ['role1', 'res1', 'action1'], + ['role2', 'res2', 'action2'], + ['role3', 'res3', 'action3'], + ['role4', 'res4', 'action4'], + ['role5', 'res5', 'action5'], + ]); + + await a.removePolicies('', 'p', [ + ['role1', 'res1', 'action1'], + ['role2', 'res2', 'action2'], + ['role3', 'res3', 'action3'], + ['role4', 'res4', 'action4'], + ['role5', 'res5', 'action5'], + ]); + e = new Enforcer(); + await e.initWithAdapter('examples/rbac_model.conf', a); + expect(await e.getPolicy()).toEqual([ + ['alice', 'data1', 'read'], + ['bob', 'data2', 'write'], + ]); + } finally { + a.close(); + } + }, + 60 * 1000, +); diff --git a/test/adapter.test.ts b/test/adapter-with-rbac-model.test.ts similarity index 100% rename from test/adapter.test.ts rename to test/adapter-with-rbac-model.test.ts