UNPKG

casbin-pg-adapter-with-schema

Version:

PostgreSQL native adapter for Node-Casbin with advanced filter capability and improved performance.

159 lines (158 loc) 5.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CasbinRepository = void 0; const pg_1 = require("pg"); class CasbinRepository { constructor(options = {}) { this.tableName = "casbin"; this.schema = "public"; this.options = options; if (options.schema) this.schema = options.schema; if (options.tableName) this.tableName = options.tableName; if (options.dbClient) { this.dbClient = options.dbClient; } else { this.db = new pg_1.Pool(options); this.dbClient = buildDbClientFactory(this.db); } } async getAllPolicies() { return this.dbClient(async (client) => { const { rows } = await client.query(`SELECT ptype, rule FROM ${this.schema}.${this.tableName}`); return rows; }); } async getFilteredPolicies(filter) { const [where, values] = buildWhereClause(filter); return this.dbClient(async (client) => { const { rows } = await client.query(`SELECT ptype, rule FROM ${this.schema}.${this.tableName}` + where, values); return rows; }); } async insertPolicy(ptype, rule) { return this.dbClient(async (client) => { await client.query(`INSERT INTO ${this.schema}.${this.tableName} (ptype, rule) VALUES ($1, $2::jsonb)`, [ptype, JSON.stringify(rule)]); }); } async insertPolicies(rules) { const req = []; const values = []; let i = 1; for (const { ptype, rule } of rules) { req.push(`($${i++}, $${i++}::jsonb)`); values.push(ptype, JSON.stringify(rule)); } return this.dbClient(async (client) => { await client.query(`INSERT INTO ${this.schema}.${this.tableName} (ptype, rule) VALUES ` + req.join(", "), values); }); } async deletePolicies(ptype, ruleFilter, fieldIndex) { const values = [ptype]; const req = `DELETE FROM ${this.schema}.${this.tableName} WHERE ptype = $${values.length} AND ` + buildRuleWhereClause(ruleFilter, values, fieldIndex); return this.dbClient(async (client) => { await client.query(req, values); }); } async clearPolicies() { return this.dbClient(async (client) => { await client.query(`DELETE FROM ${this.schema}.${this.tableName}`); }); } async open() { if (this.options.migrate !== false) { await this.migrate(); } } async migrate() { console.log("Migrating database..."); await this.dbClient(async (client) => { await client.query(` CREATE TABLE IF NOT EXISTS ${this.schema}.${this.tableName} ( id SERIAL PRIMARY KEY, ptype TEXT NOT NULL, rule JSONB NOT NULL ); `); await client.query(` CREATE INDEX IF NOT EXISTS idx_casbin_ptype ON ${this.schema}.${this.tableName} USING btree (ptype); `); }); console.log("Table and index migration completed."); } async close() { if (this.db) { await this.db.end(); } } } exports.CasbinRepository = CasbinRepository; //#region Private Functions function buildDbClientFactory(pool) { return async function batch(exec) { const client = await pool.connect(); try { const res = await Promise.resolve(exec(client)); client.release(); return res; } catch (err) { client.release(); throw err; } }; } function buildWhereClause(filter) { if (!filter) { return ["", []]; } const values = []; const res = []; Object.keys(filter).forEach(ptype => { values.push(ptype); let typePredicate = `ptype = $${values.length}`; if (filter[ptype] && filter[ptype].length) { const rulePredicate = buildRuleWhereClause(filter[ptype], values); if (rulePredicate) { typePredicate = `(${typePredicate} AND (${rulePredicate}))`; } } res.push(typePredicate); }); return [ res.length ? " WHERE " + res.join(" OR ") : "", values ]; } function buildRuleWhereClause(ruleFilter, values, fieldIndex = 0) { const res = []; ruleFilter.forEach((value, i) => { if (value === null || value === "" || typeof value === "undefined") return; if (value.startsWith("regex:")) { values.push(value.replace("regex:", "")); res.push(`rule->>${i + fieldIndex} ~ $${values.length}`); } else if (value.startsWith("like:")) { values.push(value.replace("like:", "")); res.push(`rule->>${i + fieldIndex} ~~ $${values.length}`); } else { values.push(value); res.push(`rule->>${i + fieldIndex} = $${values.length}`); } }); return res.join(" AND "); } //#endregion