role-acl
Version:
Role, Attribute and Condition based Access Control for Node.js
770 lines (769 loc) • 36.5 kB
JavaScript
"use strict";
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());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var notation_1 = __importDefault(require("notation"));
var matcher_1 = __importDefault(require("matcher"));
var array_1 = require("./array");
var conditions_1 = require("../conditions");
var core_1 = require("../core");
var lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
var CommonUtil = /** @class */ (function () {
function CommonUtil() {
}
CommonUtil.isStringOrArray = function (value) {
return typeof value === 'string' || array_1.ArrayUtil.isFilledStringArray(value);
};
CommonUtil.eachKey = function (obj, callback) {
return Object.keys(obj).forEach(callback);
};
CommonUtil.someTrue = function (elements) {
return elements.some(function (elm) { return elm; });
};
CommonUtil.allTrue = function (elements) {
return elements.every(function (elm) { return elm; });
};
CommonUtil.allFalse = function (elements) {
return elements.every(function (elm) { return !elm; });
};
CommonUtil.anyMatch = function (strings, patterns) {
var stringArray = array_1.ArrayUtil.toStringArray(strings);
var patternArray = array_1.ArrayUtil.toStringArray(patterns);
return matcher_1.default(stringArray, patternArray).length !== 0;
};
CommonUtil.toExtendedJSON = function (o) {
return JSON.stringify(o, function (key, value) {
if (typeof value === 'function') {
return '/Function(' + value.toString() + ')/';
}
return value;
});
};
CommonUtil.fromExtendedJSON = function (json) {
return JSON.parse(json, function (key, value) {
if (typeof value === 'string' &&
value.startsWith('/Function(') &&
value.endsWith(')/')) {
value = value.substring(10, value.length - 2);
return new Function('return ' + value)();
}
return value;
});
};
CommonUtil.containsPromises = function (elements) {
return elements.some(function (elm) {
return elm && typeof (elm.then) === 'function' && Promise.resolve(elm) == elm;
});
};
CommonUtil.clone = function (o) {
return lodash_clonedeep_1.default(o);
};
CommonUtil.type = function (o) {
return Object.prototype.toString.call(o).match(/\s(\w+)/i)[1].toLowerCase();
};
CommonUtil.hasDefined = function (o, propName) {
return o.hasOwnProperty(propName) && o[propName] !== undefined;
};
/**
* Gets roles and extended roles in a flat array.
*/
CommonUtil.getFlatRoles = function (grants, roles, context, skipConditions) {
return __awaiter(this, void 0, void 0, function () {
var arr, _i, roles_1, roleName, roleItem, rolesMetCondition, _a, _b, extendedRoleName, _c, _d, _e;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
roles = array_1.ArrayUtil.toStringArray(roles);
if (!roles)
throw new core_1.AccessControlError("Invalid role(s): " + JSON.stringify(roles));
arr = roles.slice();
_i = 0, roles_1 = roles;
_f.label = 1;
case 1:
if (!(_i < roles_1.length)) return [3 /*break*/, 9];
roleName = roles_1[_i];
roleItem = grants[roleName];
if (!roleItem)
throw new core_1.AccessControlError("Role not found: \"" + roleName + "\"");
if (!roleItem.$extend) return [3 /*break*/, 8];
rolesMetCondition = [];
if (!skipConditions) return [3 /*break*/, 2];
rolesMetCondition = Object.keys(roleItem.$extend);
return [3 /*break*/, 6];
case 2:
_a = 0, _b = Object.keys(roleItem.$extend);
_f.label = 3;
case 3:
if (!(_a < _b.length)) return [3 /*break*/, 6];
extendedRoleName = _b[_a];
return [4 /*yield*/, conditions_1.ConditionUtil.evaluate(roleItem.$extend[extendedRoleName].condition, context)];
case 4:
if (_f.sent()) {
rolesMetCondition.push(extendedRoleName);
}
_f.label = 5;
case 5:
_a++;
return [3 /*break*/, 3];
case 6:
_d = (_c = array_1.ArrayUtil).uniqConcat;
_e = [arr];
return [4 /*yield*/, this.getFlatRoles(grants, rolesMetCondition, context, skipConditions)];
case 7:
arr = _d.apply(_c, _e.concat([_f.sent()]));
_f.label = 8;
case 8:
_i++;
return [3 /*break*/, 1];
case 9: return [2 /*return*/, arr];
}
});
});
};
CommonUtil.getFlatRolesSync = function (grants, roles, context, skipConditions) {
roles = array_1.ArrayUtil.toStringArray(roles);
if (!roles)
throw new core_1.AccessControlError("Invalid role(s): " + JSON.stringify(roles));
var arr = roles.slice();
for (var _i = 0, roles_2 = roles; _i < roles_2.length; _i++) {
var roleName = roles_2[_i];
var roleItem = grants[roleName];
if (!roleItem)
throw new core_1.AccessControlError("Role not found: \"" + roleName + "\"");
if (roleItem.$extend) {
var rolesMetCondition = [];
if (skipConditions) {
rolesMetCondition = Object.keys(roleItem.$extend);
}
else {
for (var _a = 0, _b = Object.keys(roleItem.$extend); _a < _b.length; _a++) {
var extendedRoleName = _b[_a];
var conditionResult = conditions_1.ConditionUtil.evaluate(roleItem.$extend[extendedRoleName].condition, context);
if (typeof (conditionResult) !== 'boolean') {
throw new core_1.AccessControlError("Expected the condition function should return boolean, but returning " + conditionResult);
}
else if (conditionResult === true) {
rolesMetCondition.push(extendedRoleName);
}
}
}
arr = array_1.ArrayUtil.uniqConcat(arr, this.getFlatRolesSync(grants, rolesMetCondition, context, skipConditions));
}
}
return arr;
};
CommonUtil.normalizeGrantsObject = function (grants) {
var grantsCopy = this.clone(grants);
for (var role in grantsCopy) {
if (!grantsCopy[role].grants) {
continue;
}
grantsCopy[role].grants.forEach(function (grant) {
conditions_1.ConditionUtil.validateCondition(grant.condition);
grant.attributes = grant.attributes || ['*'];
});
grantsCopy[role].score = grantsCopy[role].score || 1;
}
return grantsCopy;
};
CommonUtil.normalizeQueryInfo = function (query) {
// clone the object
var newQuery = this.clone(query);
// validate and normalize role(s)
newQuery.role = array_1.ArrayUtil.toStringArray(newQuery.role);
if (!array_1.ArrayUtil.isFilledStringArray(newQuery.role)) {
throw new core_1.AccessControlError("Invalid role(s): " + JSON.stringify(newQuery.role));
}
// validate resource
if (newQuery.resource) {
if (typeof newQuery.resource !== 'string' || newQuery.resource.trim() === '') {
throw new core_1.AccessControlError("Invalid resource: \"" + newQuery.resource + "\"");
}
newQuery.resource = newQuery.resource.trim();
}
// validate action
if (newQuery.action) {
if (typeof newQuery.action !== 'string' || newQuery.action.trim() === '') {
throw new core_1.AccessControlError("Invalid action: " + newQuery.action);
}
}
return newQuery;
};
CommonUtil.normalizeAccessInfo = function (access) {
// clone the object
var newAccess = this.clone(access);
// validate and normalize role(s)
newAccess.role = array_1.ArrayUtil.toStringArray(newAccess.role);
if (!array_1.ArrayUtil.isFilledStringArray(newAccess.role)) {
throw new core_1.AccessControlError("Invalid role(s): " + JSON.stringify(newAccess.role));
}
// validate and normalize resource
newAccess.resource = array_1.ArrayUtil.toStringArray(newAccess.resource);
if (!array_1.ArrayUtil.isFilledStringArray(newAccess.resource)) {
throw new core_1.AccessControlError("Invalid resource(s): " + JSON.stringify(newAccess.resource));
}
// validate and normalize resource
newAccess.action = array_1.ArrayUtil.toStringArray(newAccess.action);
if (!array_1.ArrayUtil.isFilledStringArray(newAccess.action)) {
throw new core_1.AccessControlError("Invalid resource(s): " + JSON.stringify(newAccess.action));
}
newAccess.attributes = !newAccess.attributes ? ['*'] : array_1.ArrayUtil.toStringArray(newAccess.attributes);
return newAccess;
};
/**
* Used to re-set (prepare) the `attributes` of an `IAccessInfo` object
* when it's first initialized with e.g. `.grant()` or `.deny()` chain
* methods.
* @param {IAccessInfo} access
* @returns {IAccessInfo}
*/
CommonUtil.resetAttributes = function (access) {
if (!access.attributes || array_1.ArrayUtil.isEmptyArray(access.attributes)) {
access.attributes = ['*'];
}
return access;
};
/**
* Checks whether the given access info can be committed to grants model.
* @param {IAccessInfo|IQueryInfo} info
* @returns {Boolean}
*/
CommonUtil.isInfoFulfilled = function (info) {
return this.hasDefined(info, 'role')
&& this.hasDefined(info, 'action')
&& this.hasDefined(info, 'resource');
};
/**
* Commits the given `IAccessInfo` object to the grants model.
* CAUTION: if attributes is omitted, it will default to `['*']` which
* means "all attributes allowed".
* @param {Any} grants
* @param {IAccessInfo} access
* @throws {Error} If `IAccessInfo` object fails validation.
*/
CommonUtil.commitToGrants = function (grants, access) {
access = this.normalizeAccessInfo(access);
access.role.forEach(function (role) {
grants[role] = grants[role] || { score: 1 };
grants[role].grants = grants[role].grants || [];
conditions_1.ConditionUtil.validateCondition(access.condition);
grants[role].grants.push({
resource: access.resource,
action: access.action,
attributes: access.attributes,
condition: access.condition
});
});
};
CommonUtil.getUnionGrantsOfRoles = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var roles;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!grants) {
throw new core_1.AccessControlError('Grants are not set.');
}
// throws if has any invalid property value
query = this.normalizeQueryInfo(query);
return [4 /*yield*/, this.getFlatRoles(grants, query.role, query.context, query.skipConditions)];
case 1:
roles = _a.sent();
// iterate through roles and add permission attributes (array) of
// each role to attrsList (array).
return [2 /*return*/, roles.filter(function (role) {
return grants[role] && grants[role].grants;
}).map(function (role) {
return grants[role].grants;
}).reduce(function (allGrants, roleGrants) {
return allGrants.concat(roleGrants);
}, [])];
}
});
});
};
CommonUtil.getUnionGrantsOfRolesSync = function (grants, query) {
if (!grants) {
throw new core_1.AccessControlError('Grants are not set.');
}
// throws if has any invalid property value
query = this.normalizeQueryInfo(query);
// get roles and extended roles in a flat array
var roles = this.getFlatRolesSync(grants, query.role, query.context, query.skipConditions);
// iterate through roles and add permission attributes (array) of
// each role to attrsList (array).
return roles.filter(function (role) {
return grants[role] && grants[role].grants;
}).map(function (role) {
return grants[role].grants;
}).reduce(function (allGrants, roleGrants) {
return allGrants.concat(roleGrants);
}, []);
};
CommonUtil.getUnionResourcesOfRoles = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var matchingGrants;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
query.skipConditions = query.skipConditions || !query.context;
return [4 /*yield*/, this.getUnionGrantsOfRoles(grants, query)];
case 1:
matchingGrants = (_a.sent());
return [4 /*yield*/, this.filterGrantsAllowing(matchingGrants, query)];
case 2: return [2 /*return*/, (_a.sent())
.map(function (grant) {
return array_1.ArrayUtil.toStringArray(grant.resource);
}).reduce(notation_1.default.Glob.union, [])];
}
});
});
};
CommonUtil.getUnionResourcesOfRolesSync = function (grants, query) {
query.skipConditions = query.skipConditions || !query.context;
var matchingGrants = (this.getUnionGrantsOfRolesSync(grants, query));
return (this.filterGrantsAllowingSync(matchingGrants, query))
.map(function (grant) {
return array_1.ArrayUtil.toStringArray(grant.resource);
}).reduce(notation_1.default.Glob.union, []);
};
CommonUtil.getUnionActionsOfRoles = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var matchingGrants;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
query.skipConditions = query.skipConditions || !query.context;
return [4 /*yield*/, this.getUnionGrantsOfRoles(grants, query)];
case 1:
matchingGrants = (_a.sent())
.filter(function (grant) {
return _this.anyMatch(query.resource, grant.resource);
});
return [4 /*yield*/, this.filterGrantsAllowing(matchingGrants, query)];
case 2: return [2 /*return*/, (_a.sent())
.map(function (grant) {
return array_1.ArrayUtil.toStringArray(grant.action);
}).reduce(notation_1.default.Glob.union, [])];
}
});
});
};
CommonUtil.getUnionActionsOfRolesSync = function (grants, query) {
var _this = this;
query.skipConditions = query.skipConditions || !query.context;
var matchingGrants = (this.getUnionGrantsOfRolesSync(grants, query))
.filter(function (grant) {
return _this.anyMatch(query.resource, grant.resource);
});
return (this.filterGrantsAllowingSync(matchingGrants, query))
.map(function (grant) {
return array_1.ArrayUtil.toStringArray(grant.action);
}).reduce(notation_1.default.Glob.union, []);
};
/**
* When more than one role is passed, we union the permitted attributes
* for all given roles; so we can check whether "at least one of these
* roles" have the permission to execute this action.
* e.g. `can(['admin', 'user']).createAny('video')`
*
* @param {Any} grants
* @param {IQueryInfo} query
*
* @returns {Array<String>} - Array of union'ed attributes.
*/
CommonUtil.getUnionAttrsOfRoles = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var matchingGrants;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.getUnionGrantsOfRoles(grants, query)];
case 1:
matchingGrants = (_a.sent())
.filter(function (grant) {
return _this.anyMatch(query.resource, grant.resource)
&& _this.anyMatch(query.action, grant.action);
});
return [4 /*yield*/, this.filterGrantsAllowing(matchingGrants, query)];
case 2: return [2 /*return*/, (_a.sent())
.map(function (grant) {
return array_1.ArrayUtil.toStringArray(grant.attributes);
}).reduce(notation_1.default.Glob.union, [])];
}
});
});
};
CommonUtil.getUnionAttrsOfRolesSync = function (grants, query) {
var _this = this;
var matchingGrants = (this.getUnionGrantsOfRolesSync(grants, query))
.filter(function (grant) {
return _this.anyMatch(query.resource, grant.resource)
&& _this.anyMatch(query.action, grant.action);
});
return (this.filterGrantsAllowingSync(matchingGrants, query))
.map(function (grant) {
return array_1.ArrayUtil.toStringArray(grant.attributes);
}).reduce(notation_1.default.Glob.union, []);
};
CommonUtil.filterGrantsAllowing = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var matchingGrants, _i, grants_1, grant;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!query.skipConditions) return [3 /*break*/, 1];
return [2 /*return*/, grants];
case 1:
matchingGrants = [];
_i = 0, grants_1 = grants;
_a.label = 2;
case 2:
if (!(_i < grants_1.length)) return [3 /*break*/, 5];
grant = grants_1[_i];
return [4 /*yield*/, conditions_1.ConditionUtil.evaluate(grant.condition, query.context)];
case 3:
if (_a.sent()) {
matchingGrants.push(grant);
}
_a.label = 4;
case 4:
_i++;
return [3 /*break*/, 2];
case 5: return [2 /*return*/, matchingGrants];
}
});
});
};
CommonUtil.filterGrantsAllowingSync = function (grants, query) {
if (query.skipConditions) {
return grants;
}
else {
var matchingGrants = [];
for (var _i = 0, grants_2 = grants; _i < grants_2.length; _i++) {
var grant = grants_2[_i];
var conditionResult = query.skipConditions || conditions_1.ConditionUtil.evaluate(grant.condition, query.context);
if (typeof (conditionResult) !== 'boolean') {
throw new core_1.AccessControlError("Expected the condition function should return boolean, but returning " + conditionResult);
}
if (conditionResult) {
matchingGrants.push(grant);
}
}
return matchingGrants;
}
};
CommonUtil.areGrantsAllowing = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var result, _i, grants_3, grant, _a, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (!grants) {
return [2 /*return*/, false];
}
result = false;
_i = 0, grants_3 = grants;
_d.label = 1;
case 1:
if (!(_i < grants_3.length)) return [3 /*break*/, 7];
grant = grants_3[_i];
_a = result;
if (_a) return [3 /*break*/, 5];
_b = this.anyMatch(query.resource, grant.resource)
&& this.anyMatch(query.action, grant.action);
if (!_b) return [3 /*break*/, 4];
_c = query.skipConditions;
if (_c) return [3 /*break*/, 3];
return [4 /*yield*/, conditions_1.ConditionUtil.evaluate(grant.condition, query.context)];
case 2:
_c = (_d.sent());
_d.label = 3;
case 3:
_b = (_c);
_d.label = 4;
case 4:
_a = (_b);
_d.label = 5;
case 5:
result = _a;
_d.label = 6;
case 6:
_i++;
return [3 /*break*/, 1];
case 7: return [2 /*return*/, result];
}
});
});
};
CommonUtil.areGrantsAllowingSync = function (grants, query) {
if (!grants) {
return false;
}
var result = false;
for (var _i = 0, grants_4 = grants; _i < grants_4.length; _i++) {
var grant = grants_4[_i];
var conditionResult = query.skipConditions || conditions_1.ConditionUtil.evaluate(grant.condition, query.context);
if (typeof (conditionResult) !== 'boolean') {
throw new core_1.AccessControlError("Expected the condition function should return boolean, but returning " + conditionResult);
}
result = result || (this.anyMatch(query.resource, grant.resource)
&& this.anyMatch(query.action, grant.action)
&& (query.skipConditions || conditionResult));
}
return result;
};
CommonUtil.areExtendingRolesAllowing = function (roleExtensionObject, allowingRoles, query) {
return __awaiter(this, void 0, void 0, function () {
var result, _a, _b, _i, roleName, _c, _d, _e;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
if (!roleExtensionObject) {
return [2 /*return*/, false];
}
result = false;
_a = [];
for (_b in roleExtensionObject)
_a.push(_b);
_i = 0;
_f.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 7];
roleName = _a[_i];
_c = result;
if (_c) return [3 /*break*/, 5];
_d = allowingRoles[roleName];
if (!_d) return [3 /*break*/, 4];
_e = query.skipConditions;
if (_e) return [3 /*break*/, 3];
return [4 /*yield*/, conditions_1.ConditionUtil.evaluate(roleExtensionObject[roleName].condition, query.context)];
case 2:
_e = (_f.sent());
_f.label = 3;
case 3:
_d = (_e);
_f.label = 4;
case 4:
_c = (_d);
_f.label = 5;
case 5:
result = _c;
_f.label = 6;
case 6:
_i++;
return [3 /*break*/, 1];
case 7: return [2 /*return*/, result];
}
});
});
};
CommonUtil.areExtendingRolesAllowingSync = function (roleExtensionObject, allowingRoles, query) {
if (!roleExtensionObject) {
return false;
}
var result = false;
for (var roleName in roleExtensionObject) {
var conditionResult = query.skipConditions || conditions_1.ConditionUtil.evaluate(roleExtensionObject[roleName].condition, query.context);
if (typeof (conditionResult) !== 'boolean') {
throw new core_1.AccessControlError("Expected the condition function should return boolean, but returning " + conditionResult);
}
result = result || (allowingRoles[roleName] && (query.skipConditions || conditionResult));
}
return result;
};
CommonUtil.getAllowingRoles = function (grants, query) {
return __awaiter(this, void 0, void 0, function () {
var allowingRoles, sortedRoles, _i, sortedRoles_1, role, _a, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (!grants) {
throw new core_1.AccessControlError('Grants are not set.');
}
allowingRoles = {};
sortedRoles = Object.keys(grants).sort(function (role1, role2) {
return grants[role1].score - grants[role2].score;
});
_i = 0, sortedRoles_1 = sortedRoles;
_d.label = 1;
case 1:
if (!(_i < sortedRoles_1.length)) return [3 /*break*/, 6];
role = sortedRoles_1[_i];
_a = allowingRoles;
_b = role;
return [4 /*yield*/, this.areGrantsAllowing(grants[role].grants, query)];
case 2:
_c = (_d.sent());
if (_c) return [3 /*break*/, 4];
return [4 /*yield*/, this.areExtendingRolesAllowing(grants[role].$extend, allowingRoles, query)];
case 3:
_c = (_d.sent());
_d.label = 4;
case 4:
_a[_b] = _c;
_d.label = 5;
case 5:
_i++;
return [3 /*break*/, 1];
case 6: return [2 /*return*/, Object.keys(allowingRoles).filter(function (role) {
return allowingRoles[role];
})];
}
});
});
};
CommonUtil.getAllowingRolesSync = function (grants, query) {
if (!grants) {
throw new core_1.AccessControlError('Grants are not set.');
}
var allowingRoles = {};
var sortedRoles = Object.keys(grants).sort(function (role1, role2) {
return grants[role1].score - grants[role2].score;
});
for (var _i = 0, sortedRoles_2 = sortedRoles; _i < sortedRoles_2.length; _i++) {
var role = sortedRoles_2[_i];
allowingRoles[role] = this.areGrantsAllowingSync(grants[role].grants, query) ||
this.areExtendingRolesAllowingSync(grants[role].$extend, allowingRoles, query);
}
return Object.keys(allowingRoles).filter(function (role) {
return allowingRoles[role];
});
};
/**
* Checks the given grants model and gets an array of non-existent roles
* from the given roles.
* @param {Any} grants - Grants model to be checked.
* @param {Array<string>} roles - Roles to be checked.
* @returns {Array<String>} - Array of non-existent roles. Empty array if
* all exist.
*/
CommonUtil.getNonExistentRoles = function (grants, roles) {
var non = [];
for (var _i = 0, roles_3 = roles; _i < roles_3.length; _i++) {
var role = roles_3[_i];
if (!grants.hasOwnProperty(role))
non.push(role);
}
return non;
};
/**
* Extends the given role(s) with privileges of one or more other roles.
*
* @param {Any} grants
* @param {String|Array<String>} roles
* Role(s) to be extended.
* Single role as a `String` or multiple roles as an `Array`.
* Note that if a role does not exist, it will be automatically
* created.
*
* @param {String|Array<String>} extenderRoles
* Role(s) to inherit from.
* Single role as a `String` or multiple roles as an `Array`.
* Note that if a extender role does not exist, it will throw.
* @param {ICondition} [condition]
* Condition to be used for extension of roles. Only extends
* the roles when condition is met
*
* @throws {Error}
* If a role is extended by itself or a non-existent role.
*/
CommonUtil.extendRole = function (grants, roles, extenderRoles, condition) {
conditions_1.ConditionUtil.validateCondition(condition);
CommonUtil.extendRoleSync(grants, roles, extenderRoles, condition);
};
CommonUtil.extendRoleSync = function (grants, roles, extenderRoles, condition) {
conditions_1.ConditionUtil.validateCondition(condition);
var arrExtRoles = array_1.ArrayUtil.toStringArray(extenderRoles);
if (!arrExtRoles)
throw new core_1.AccessControlError("Invalid extender role(s): " + JSON.stringify(extenderRoles));
var nonExistentExtRoles = this.getNonExistentRoles(grants, arrExtRoles);
if (nonExistentExtRoles.length > 0) {
throw new core_1.AccessControlError("Cannot extend with non-existent role(s): \"" + nonExistentExtRoles.join(', ') + "\"");
}
roles = array_1.ArrayUtil.toStringArray(roles);
if (!roles)
throw new core_1.AccessControlError("Invalid role(s): " + JSON.stringify(roles));
var allExtendingRoles = this.getFlatRolesSync(grants, arrExtRoles, null, true);
var extensionScore = allExtendingRoles.reduce(function (total, role) {
return total + grants[role].score;
}, 0);
roles.forEach(function (role) {
if (allExtendingRoles.indexOf(role) >= 0) {
throw new core_1.AccessControlError("Attempted to extend role \"" + role + "\" by itself.");
}
grants[role] = grants[role] || { score: 1 };
grants[role].score += extensionScore;
grants[role].$extend = grants[role].$extend || {};
arrExtRoles.forEach(function (extRole) {
grants[role].$extend[extRole] = grants[role].$extend[extRole] || {};
grants[role].$extend[extRole].condition = condition;
});
});
};
CommonUtil.matchesAllElement = function (values, predicateFn) {
values = array_1.ArrayUtil.toArray(values);
return values.every(predicateFn);
};
CommonUtil.matchesAnyElement = function (values, predicateFn) {
values = array_1.ArrayUtil.toArray(values);
return values.some(predicateFn);
};
CommonUtil.filter = function (object, attributes) {
if (!Array.isArray(attributes) || attributes.length === 0) {
return {};
}
var notation = new notation_1.default(object);
return notation.filter(attributes).value;
};
CommonUtil.filterAll = function (arrOrObj, attributes) {
var _this = this;
if (!Array.isArray(arrOrObj)) {
return this.filter(arrOrObj, attributes);
}
return arrOrObj.map(function (o) {
return _this.filter(o, attributes);
});
};
return CommonUtil;
}());
exports.CommonUtil = CommonUtil;