casbin
Version:
An authorization library that supports access control models like ACL, RBAC, ABAC in Node.JS
389 lines (388 loc) • 13.3 kB
JavaScript
;
// 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.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.newModelFromString = exports.newModelFromFile = exports.newModel = exports.Model = exports.requiredSections = exports.PolicyOp = exports.sectionNameMap = void 0;
const util = require("../util");
const config_1 = require("../config");
const assertion_1 = require("./assertion");
const log_1 = require("../log");
exports.sectionNameMap = {
r: 'request_definition',
p: 'policy_definition',
g: 'role_definition',
e: 'policy_effect',
m: 'matchers'
};
var PolicyOp;
(function (PolicyOp) {
PolicyOp[PolicyOp["PolicyAdd"] = 0] = "PolicyAdd";
PolicyOp[PolicyOp["PolicyRemove"] = 1] = "PolicyRemove";
})(PolicyOp = exports.PolicyOp || (exports.PolicyOp = {}));
exports.requiredSections = ['r', 'p', 'e', 'm'];
class Model {
/**
* constructor is the constructor for Model.
*/
constructor() {
this.model = new Map();
}
loadAssertion(cfg, sec, key) {
const secName = exports.sectionNameMap[sec];
const value = cfg.getString(`${secName}::${key}`);
return this.addDef(sec, key, value);
}
getKeySuffix(i) {
if (i === 1) {
return '';
}
return i.toString();
}
loadSection(cfg, sec) {
let i = 1;
for (;;) {
if (!this.loadAssertion(cfg, sec, sec + this.getKeySuffix(i))) {
break;
}
else {
i++;
}
}
}
// addDef adds an assertion to the model.
addDef(sec, key, value) {
if (value === '') {
return false;
}
const ast = new assertion_1.Assertion();
ast.key = key;
ast.value = value;
if (sec === 'r' || sec === 'p') {
const tokens = value.split(',').map(n => n.trim());
for (let i = 0; i < tokens.length; i++) {
tokens[i] = key + '_' + tokens[i];
}
ast.tokens = tokens;
}
else if (sec === 'm') {
const stringArguments = value.match(/\"(.*?)\"/g) || [];
stringArguments.forEach((n, index) => {
value = value.replace(n, `$<${index}>`);
});
value = util.escapeAssertion(value);
stringArguments.forEach((n, index) => {
value = value.replace(`$<${index}>`, n);
});
ast.value = value;
}
else {
ast.value = util.escapeAssertion(value);
}
const nodeMap = this.model.get(sec);
if (nodeMap) {
nodeMap.set(key, ast);
}
else {
const assertionMap = new Map();
assertionMap.set(key, ast);
this.model.set(sec, assertionMap);
}
return true;
}
// loadModel loads the model from model CONF file.
loadModel(path) {
const cfg = config_1.Config.newConfig(path);
this.loadModelFromConfig(cfg);
}
// loadModelFromText loads the model from the text.
loadModelFromText(text) {
const cfg = config_1.Config.newConfigFromText(text);
this.loadModelFromConfig(cfg);
}
loadModelFromConfig(cfg) {
for (const s in exports.sectionNameMap) {
this.loadSection(cfg, s);
}
const ms = [];
exports.requiredSections.forEach(n => {
if (!this.hasSection(n)) {
ms.push(exports.sectionNameMap[n]);
}
});
if (ms.length > 0) {
throw new Error(`missing required sections: ${ms.join(',')}`);
}
}
hasSection(sec) {
return this.model.get(sec) !== undefined;
}
// printModel prints the model to the log.
printModel() {
log_1.logPrint('Model:');
this.model.forEach((value, key) => {
value.forEach((ast, astKey) => {
log_1.logPrint(`${key}.${astKey}: ${ast.value}`);
});
});
}
// buildIncrementalRoleLinks provides incremental build the role inheritance relations.
buildIncrementalRoleLinks(rm, op, sec, ptype, rules) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (sec === 'g') {
yield ((_b = (_a = this.model
.get(sec)) === null || _a === void 0 ? void 0 : _a.get(ptype)) === null || _b === void 0 ? void 0 : _b.buildIncrementalRoleLinks(rm, op, rules));
}
});
}
// buildRoleLinks initializes the roles in RBAC.
buildRoleLinks(rm) {
return __awaiter(this, void 0, void 0, function* () {
const astMap = this.model.get('g');
if (!astMap) {
return;
}
for (const value of astMap.values()) {
yield value.buildRoleLinks(rm);
}
});
}
// clearPolicy clears all current policy.
clearPolicy() {
this.model.forEach((value, key) => {
if (key === 'p' || key === 'g') {
value.forEach(ast => {
ast.policy = [];
});
}
});
}
// getPolicy gets all rules in a policy.
getPolicy(sec, key) {
var _a;
const policy = [];
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (ast) {
policy.push(...ast.policy);
}
return policy;
}
// hasPolicy determines whether a model has the specified policy rule.
hasPolicy(sec, key, rule) {
var _a;
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (!ast) {
return false;
}
return ast.policy.some((n) => util.arrayEquals(n, rule));
}
// addPolicy adds a policy rule to the model.
addPolicy(sec, key, rule) {
var _a;
if (!this.hasPolicy(sec, key, rule)) {
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (!ast) {
return false;
}
ast.policy.push(rule);
return true;
}
return false;
}
// addPolicies adds policy rules to the model.
addPolicies(sec, ptype, rules) {
var _a;
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(ptype);
if (!ast) {
return [false, []];
}
for (const rule of rules) {
if (this.hasPolicy(sec, ptype, rule)) {
return [false, []];
}
}
ast.policy = ast.policy.concat(rules);
return [true, rules];
}
// removePolicy removes a policy rule from the model.
removePolicy(sec, key, rule) {
var _a;
if (this.hasPolicy(sec, key, rule)) {
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (!ast) {
return false;
}
ast.policy = ast.policy.filter(r => !util.arrayEquals(rule, r));
return true;
}
return false;
}
// removePolicies removes policy rules from the model.
removePolicies(sec, ptype, rules) {
var _a;
const effects = [];
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(ptype);
if (!ast) {
return [false, []];
}
for (const rule of rules) {
if (!this.hasPolicy(sec, ptype, rule)) {
return [false, []];
}
}
for (const rule of rules) {
ast.policy = ast.policy.filter((r) => {
const equals = util.arrayEquals(rule, r);
if (equals) {
effects.push(r);
}
return !equals;
});
}
return [true, effects];
}
// getFilteredPolicy gets rules based on field filters from a policy.
getFilteredPolicy(sec, key, fieldIndex, ...fieldValues) {
var _a;
const res = [];
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (!ast) {
return res;
}
for (const rule of ast.policy) {
let matched = true;
for (let i = 0; i < fieldValues.length; i++) {
const fieldValue = fieldValues[i];
if (fieldValue !== '' && rule[fieldIndex + i] !== fieldValue) {
matched = false;
break;
}
}
if (matched) {
res.push(rule);
}
}
return res;
}
// removeFilteredPolicy removes policy rules based on field filters from the model.
removeFilteredPolicy(sec, key, fieldIndex, ...fieldValues) {
var _a;
const res = [];
const effects = [];
let bool = false;
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (!ast) {
return [false, []];
}
for (const rule of ast.policy) {
let matched = true;
for (let i = 0; i < fieldValues.length; i++) {
const fieldValue = fieldValues[i];
if (fieldValue !== '' && rule[fieldIndex + i] !== fieldValue) {
matched = false;
break;
}
}
if (matched) {
bool = true;
effects.push(rule);
}
else {
res.push(rule);
}
}
ast.policy = res;
return [bool, effects];
}
// getValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed.
getValuesForFieldInPolicy(sec, key, fieldIndex) {
var _a;
const values = [];
const ast = (_a = this.model.get(sec)) === null || _a === void 0 ? void 0 : _a.get(key);
if (!ast) {
return values;
}
return util.arrayRemoveDuplicates(ast.policy.map((n) => n[fieldIndex]));
}
// getValuesForFieldInPolicyAllTypes gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed.
getValuesForFieldInPolicyAllTypes(sec, fieldIndex) {
const values = [];
const ast = this.model.get(sec);
if (!ast) {
return values;
}
for (const ptype of ast.keys()) {
values.push(...this.getValuesForFieldInPolicy(sec, ptype, fieldIndex));
}
return util.arrayRemoveDuplicates(values);
}
// printPolicy prints the policy to log.
printPolicy() {
log_1.logPrint('Policy:');
this.model.forEach((map, key) => {
if (key === 'p' || key === 'g') {
map.forEach(ast => {
log_1.logPrint(`key, : ${ast.value}, : , ${ast.policy}`);
});
}
});
}
}
exports.Model = Model;
/**
* newModel creates a model.
*/
function newModel(...text) {
const m = new Model();
if (text.length === 2) {
if (text[0] !== '') {
m.loadModel(text[0]);
}
}
else if (text.length === 1) {
m.loadModelFromText(text[0]);
}
else if (text.length !== 0) {
throw new Error('Invalid parameters for model.');
}
return m;
}
exports.newModel = newModel;
/**
* newModelFromFile creates a model from a .CONF file.
*/
function newModelFromFile(path) {
const m = new Model();
m.loadModel(path);
return m;
}
exports.newModelFromFile = newModelFromFile;
/**
* newModelFromString creates a model from a string which contains model text.
*/
function newModelFromString(text) {
const m = new Model();
m.loadModelFromText(text);
return m;
}
exports.newModelFromString = newModelFromString;