Migrate to sequelize v5
diff --git a/.travis.yml b/.travis.yml index 2fcb0df..da14dc2 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -5,7 +5,6 @@ node_js: - 'stable' - '8' - - '6' install: yarn install cache: @@ -17,7 +16,10 @@ include: - stage: Produce Coverage node_js: node - script: jest --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage + script: jest --coverage --forceExit && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage + +before_install: + - mysql -e 'CREATE DATABASE casbin;' services: - mysql
diff --git a/README.md b/README.md index 862e921..b7ccd5a 100644 --- a/README.md +++ b/README.md
@@ -35,20 +35,18 @@ async function myFunction() { // Initialize a Sequelize adapter and use it in a Node-Casbin enforcer: - // The adapter will use the MySQL database named "casbin". - // If it doesn't exist, the adapter will create it automatically. - const a = await SequelizeAdapter.newAdapter('mysql://root:123@localhost:3306/'); // Your connection string. - - // Or you can use an existing DB "abc" like this: - // The adapter will use the table named "casbin_rule". - // If it doesn't exist, the adapter will create it automatically. - // const a = await SequelizeAdapter.newAdapter('mysql://root:123@localhost:3306/abc', true); + // The adapter can not automatically create database. + // But the adapter will automatically and use the table named "casbin_rule". + // ORM should not create databases automatically. + const a = await SequelizeAdapter.newAdapter({ + username: 'root', + password: '', + database: 'casbin', + dialect: 'mysql' + }); const e = await casbin.newEnforcer('examples/rbac_model.conf', a); - // Load the policy from DB. - await e.loadPolicy(); - // Check the permission. e.enforce('alice', 'data1', 'read');
diff --git a/package.json b/package.json index caa6891..6ade9f3 100644 --- a/package.json +++ b/package.json
@@ -10,29 +10,27 @@ "build": "rimraf lib && tsc", "lint": "tslint \"src/**/*.ts\"", "fix": "tslint \"src/**/*.ts\" --fix", - "test": "jest" + "test": "jest --forceExit" }, "devDependencies": { - "@types/jest": "^23.3.1", - "@types/lodash": "^4.14.113", + "@types/jest": "^24.0.15", "@types/node": "^10.5.3", - "@types/sequelize": "^4.27.24", + "@types/sequelize": "^4.28.3", "coveralls": "^3.0.2", "husky": "^0.14.3", - "jest": "^24.1.0", + "jest": "^24.8.0", "lint-staged": "^7.2.0", "mysql2": "^1.6.1", "rimraf": "^2.6.2", - "ts-jest": "22.4.6", + "ts-jest": "^24.0.2", "tslint": "^5.11.0", "typescript": "^3.3.3" }, "dependencies": { - "casbin": "^2.0.0", - "lodash": "^4.17.10", + "casbin": "^3.0.1", "reflect-metadata": "^0.1.12", - "sequelize": "^4.42.0", - "sequelize-typescript": "^0.6.7" + "sequelize": "^5.8.10", + "sequelize-typescript": "^1.0.0-beta.3" }, "files": [ "lib",
diff --git a/src/adapter.ts b/src/adapter.ts index 1063c64..a86f110 100644 --- a/src/adapter.ts +++ b/src/adapter.ts
@@ -13,77 +13,35 @@ // limitations under the License. import {Adapter, Helper, Model} from 'casbin'; -import {Sequelize, ISequelizeUriConfig} from 'sequelize-typescript'; +import {Sequelize, SequelizeOptions} from 'sequelize-typescript'; import {CasbinRule} from './casbinRule'; -function ModelFactory(model: typeof CasbinRule) { - return class extends model { - }; -} - /** * SequelizeAdapter represents the Sequelize adapter for policy storage. */ export class SequelizeAdapter implements Adapter { - private connStr: string; - private dbSpecified: boolean; - + private option: SequelizeOptions; private sequelize: Sequelize; - private static modelMap: Map<Sequelize, typeof CasbinRule> = new Map(); - constructor(connStr: string, dbSpecified: boolean) { - this.connStr = connStr; - this.dbSpecified = dbSpecified; + constructor(option: SequelizeOptions) { + this.option = option; } /** * newAdapter is the constructor. - * dbSpecified is an optional boolean parameter. The default value is false. - * It's up to whether you have specified an existing DB in connStr. - * If dbSpecified == true, you need to make sure the DB in connStr exists. - * If dbSpecified == false, the adapter will automatically create a DB named 'casbin'. + * @param option sequelize connection option */ - public static async newAdapter(connStr: string, dbSpecified: boolean = false) { - const a = new SequelizeAdapter(connStr, dbSpecified); + public static async newAdapter(option: SequelizeOptions) { + const a = new SequelizeAdapter(option); await a.open(); return a; } - private async createDatabase() { - const uriConfig: ISequelizeUriConfig = { - url: this.connStr, - logging: false, - pool: {max: 5, min: 0, idle: 10000} - }; - const sequelize = new Sequelize(uriConfig); - await sequelize.authenticate(); - - await sequelize.query('CREATE DATABASE IF NOT EXISTS casbin'); - - await sequelize.close(); - } - private async open() { - let url = this.connStr; - if (!this.dbSpecified) { - url = this.connStr + 'casbin'; - await this.createDatabase(); - } - - const uriConfig: ISequelizeUriConfig = { - url, - logging: false, - pool: {max: 5, min: 0, idle: 10000} - }; - - this.sequelize = new Sequelize(uriConfig); + this.sequelize = new Sequelize(this.option); await this.sequelize.authenticate(); - - const Rule = ModelFactory(CasbinRule); - SequelizeAdapter.modelMap.set(this.sequelize, Rule); - this.sequelize.addModels([Rule]); - + this.sequelize.addModels([CasbinRule]); await this.createTable(); } @@ -91,17 +49,12 @@ await this.sequelize.close(); } - private getCasbinRuleModel(): typeof CasbinRule { - const model = SequelizeAdapter.modelMap.get(this.sequelize); - return !model ? CasbinRule : model; - } - private async createTable() { - await this.getCasbinRuleModel().sync(); + await this.sequelize.sync(); } private async dropTable() { - await this.getCasbinRuleModel().destroy({where: {}, truncate: true}); + await this.sequelize.getRepository(CasbinRule).destroy({where: {}, truncate: true}); } private loadPolicyLine(line: CasbinRule, model: Model) { @@ -113,7 +66,7 @@ * loadPolicy loads all policy rules from the storage. */ public async loadPolicy(model: Model) { - const lines = await this.getCasbinRuleModel().findAll(); + const lines = await this.sequelize.getRepository(CasbinRule).findAll(); for (const line of lines) { this.loadPolicyLine(line, model); @@ -121,8 +74,7 @@ } private savePolicyLine(ptype: string, rule: string[]): CasbinRule { - const Rule = this.getCasbinRuleModel(); - const line = new Rule(); + const line = new CasbinRule(); line.ptype = ptype; if (rule.length > 0) { @@ -154,8 +106,7 @@ await this.dropTable(); await this.createTable(); - let astMap = model.model.get('p'); - // @ts-ignore + let astMap = model.model.get('p')!; for (const [ptype, ast] of astMap) { for (const rule of ast.policy) { const line = this.savePolicyLine(ptype, rule); @@ -163,8 +114,7 @@ } } - astMap = model.model.get('g'); - // @ts-ignore + astMap = model.model.get('g')!; for (const [ptype, ast] of astMap) { for (const rule of ast.policy) { const line = this.savePolicyLine(ptype, rule); @@ -189,15 +139,15 @@ public async removePolicy(sec: string, ptype: string, rule: string[]) { const line = this.savePolicyLine(ptype, rule); const where = {}; - Object.keys(line.dataValues) + + Object.keys(line.get({plain: true})) .filter(key => key !== 'id') .forEach(key => { // @ts-ignore where[key] = line[key]; }); - // @ts-ignore - await this.getCasbinRuleModel().destroy({where}); + await this.sequelize.getRepository(CasbinRule).destroy({where}); } /**
diff --git a/src/casbinRule.ts b/src/casbinRule.ts index c516cf6..3b76d8c 100644 --- a/src/casbinRule.ts +++ b/src/casbinRule.ts
@@ -14,7 +14,7 @@ import {Table, Column, Model} from 'sequelize-typescript'; -@Table({tableName: 'casbin_rule'}) +@Table({tableName: 'casbin_rule', timestamps: false}) export class CasbinRule extends Model<CasbinRule> { @Column public ptype: string;
diff --git a/test/adapter.test.ts b/test/adapter.test.ts index ed15fc6..e6c847d 100644 --- a/test/adapter.test.ts +++ b/test/adapter.test.ts
@@ -23,7 +23,13 @@ } test('TestAdapter', async () => { - const a = await SequelizeAdapter.newAdapter('mysql://root:@localhost:3306/'); + const a = await SequelizeAdapter.newAdapter({ + username: 'root', + password: '', + database: 'casbin', + dialect: 'mysql' + }); + try { // Because the DB is empty at first, // so we need to load the policy from the file adapter (.CSV) first.