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
JavaScript
;
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