@geexbox/accesscontrol
Version:
Subject and Attribute based Access Control for Node.js
476 lines (475 loc) • 20.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("../core");
var utils_1 = require("../utils");
/**
* Represents the inner `Access` class that helps build an access information
* to be granted or denied; and finally commits it to the underlying grants
* model. You can get a first instance of this class by calling
* `AccessControl#grant()` or `AccessControl#deny()` methods.
* @class
* @inner
* @memberof AccessControl
*/
var Access = /** @class */ (function () {
/**
* Initializes a new instance of `Access`.
* @private
*
* @param {AccessControl} ac
* AccessControl instance.
* @param {String|Array<String>|IAccessInfo} [subjectOrInfo]
* Either an `IAccessInfo` object, a single or an array of
* subjects. If an object is passed, possession and attributes
* properties are optional. CAUTION: if attributes is omitted,
* and access is not denied, it will default to `["*"]` which means
* "all attributes allowed". If possession is omitted, it will
* default to `"any"`.
* @param {Boolean} denied
* Specifies whether this `Access` is denied.
*/
function Access(ac, subjectOrInfo, denied) {
if (denied === void 0) { denied = false; }
/**
* Inner `IAccessInfo` object.
* @protected
* @type {IAccessInfo}
*/
this._ = {};
this._ac = ac;
this._grants = ac._grants;
this._.denied = denied;
if (typeof subjectOrInfo === 'string' || Array.isArray(subjectOrInfo)) {
this.subject(subjectOrInfo);
}
else if (utils_1.utils.type(subjectOrInfo) === 'object') {
if (Object.keys(subjectOrInfo).length === 0) {
throw new core_1.AccessControlError('Invalid IAccessInfo: {}');
}
// if an IAccessInfo instance is passed and it has 'action' defined, we
// should directly commit it to grants.
subjectOrInfo.denied = denied;
this._ = utils_1.utils.resetAttributes(subjectOrInfo);
if (utils_1.utils.isInfoFulfilled(this._))
utils_1.utils.commitToGrants(this._grants, this._, true);
}
else if (subjectOrInfo !== undefined) {
// undefined is allowed (`subjectOrInfo` can be omitted) but throw if
// some other type is passed.
throw new core_1.AccessControlError('Invalid subject(s), expected a valid string, string[] or IAccessInfo.');
}
}
Object.defineProperty(Access.prototype, "denied", {
// -------------------------------
// PUBLIC PROPERTIES
// -------------------------------
/**
* Specifies whether this access is initally denied.
* @name AccessControl~Access#denied
* @type {Boolean}
* @readonly
*/
get: function () {
return this._.denied;
},
enumerable: true,
configurable: true
});
// -------------------------------
// PUBLIC METHODS
// -------------------------------
/**
* A chainer method that sets the subject(s) for this `Access` instance.
* @param {String|Array<String>} value
* A single or array of subjects.
* @returns {Access}
* Self instance of `Access`.
*/
Access.prototype.subject = function (value) {
// in case chain is not terminated (e.g. `ac.grant('user')`) we'll
// create/commit the subjects to grants with an empty object.
utils_1.utils.preCreateRoles(this._grants, value);
this._.subject = value;
return this;
};
/**
* A chainer method that sets the resource for this `Access` instance.
* @param {String|Array<String>} value
* Target resource for this `Access` instance.
* @returns {Access}
* Self instance of `Access`.
*/
Access.prototype.resource = function (value) {
// this will throw if any item fails
utils_1.utils.hasValidNames(value, true);
this._.resource = value;
return this;
};
/**
* Sets the array of allowed attributes for this `Access` instance.
* @param {String|Array<String>} value
* Attributes to be set.
* @returns {Access}
* Self instance of `Access`.
*/
Access.prototype.attributes = function (value) {
this._.attributes = value;
return this;
};
/**
* Sets the subjects to be extended for this `Access` instance.
* @alias Access#inherit
* @name AccessControl~Access#extend
* @function
*
* @param {String|Array<String>} subjects
* A single or array of subjects.
* @returns {Access}
* Self instance of `Access`.
*
* @example
* ac.grant('user').createAny('video')
* .grant('admin').extend('user');
* const permission = ac.can('admin').createAny('video');
* console.log(permission.granted); // true
*/
Access.prototype.extend = function (subjects) {
utils_1.utils.extendRole(this._grants, this._.subject, subjects, false);
return this;
};
/**
* Alias of `extend`.
* @private
*/
Access.prototype.inherit = function (subjects) {
this.extend(subjects);
return this;
};
/**
* Shorthand to switch to a new `Access` instance with a different subject
* within the method chain.
*
* @param {String|Array<String>|IAccessInfo} [subjectOrInfo]
* Either a single or an array of subjects or an
* {@link ?api=ac#AccessControl~IAccessInfo|`IAccessInfo` object}.
*
* @returns {Access}
* A new `Access` instance.
*
* @example
* ac.grant('user').createOwn('video')
* .grant('admin').updateAny('video');
*/
Access.prototype.grant = function (subjectOrInfo) {
return (new Access(this._ac, subjectOrInfo, false)).attributes(['*']);
};
/**
* Shorthand to switch to a new `Access` instance with a different
* (or same) subject within the method chain.
*
* @param {String|Array<String>|IAccessInfo} [subjectOrInfo]
* Either a single or an array of subjects or an
* {@link ?api=ac#AccessControl~IAccessInfo|`IAccessInfo` object}.
*
* @returns {Access}
* A new `Access` instance.
*
* @example
* ac.grant('admin').createAny('video')
* .deny('user').deleteAny('video');
*/
Access.prototype.deny = function (subjectOrInfo) {
return (new Access(this._ac, subjectOrInfo, true)).attributes([]);
};
/**
* Chainable, convenience shortcut for {@link ?api=ac#AccessControl#lock|`AccessControl#lock()`}.
* @returns {Access}
*/
Access.prototype.lock = function () {
utils_1.utils.lockAC(this._ac);
return this;
};
/**
* Sets the action to `"create"` and possession to `"own"` and commits the
* current access instance to the underlying grant model.
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid
* data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.createOwn = function (resource, attributes) {
return this._prepareAndCommit('create', 'own', resource, attributes);
};
/**
* Sets the action to `"create"` and possession to `"any"` and commits the
* current access instance to the underlying grant model.
* @alias Access#create
* @name AccessControl~Access#createAny
* @function
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.createAny = function (resource, attributes) {
return this._prepareAndCommit('create', 'any', resource, attributes);
};
/**
* Alias of `createAny`
* @private
*/
Access.prototype.create = function (resource, attributes) {
return this.createAny(resource, attributes);
};
/**
* Sets the action to `"read"` and possession to `"own"` and commits the
* current access instance to the underlying grant model.
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.readOwn = function (resource, attributes) {
return this._prepareAndCommit('read', 'own', resource, attributes);
};
/**
* Sets the action to `"read"` and possession to `"any"` and commits the
* current access instance to the underlying grant model.
* @alias Access#read
* @name AccessControl~Access#readAny
* @function
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.readAny = function (resource, attributes) {
return this._prepareAndCommit('read', 'any', resource, attributes);
};
/**
* Alias of `readAny`
* @private
*/
Access.prototype.read = function (resource, attributes) {
return this.readAny(resource, attributes);
};
/**
* Sets the action to `"update"` and possession to `"own"` and commits the
* current access instance to the underlying grant model.
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.updateOwn = function (resource, attributes) {
return this._prepareAndCommit('update', 'own', resource, attributes);
};
/**
* Sets the action to `"update"` and possession to `"any"` and commits the
* current access instance to the underlying grant model.
* @alias Access#update
* @name AccessControl~Access#updateAny
* @function
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.updateAny = function (resource, attributes) {
return this._prepareAndCommit('update', 'any', resource, attributes);
};
/**
* Alias of `updateAny`
* @private
*/
Access.prototype.update = function (resource, attributes) {
return this.updateAny(resource, attributes);
};
/**
* Sets the action to `"delete"` and possession to `"own"` and commits the
* current access instance to the underlying grant model.
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.deleteOwn = function (resource, attributes) {
return this._prepareAndCommit('delete', 'own', resource, attributes);
};
/**
* Sets the action to `"delete"` and possession to `"any"` and commits the
* current access instance to the underlying grant model.
* @alias Access#delete
* @name AccessControl~Access#deleteAny
* @function
*
* @param {String|Array<String>} [resource]
* Defines the target resource this access is granted or denied for.
* This is only optional if the resource is previously defined.
* If not defined and omitted, this will throw.
* @param {String|Array<String>} [attributes]
* Defines the resource attributes for which the access is granted
* for. If access is denied previously by calling `.deny()` this
* will default to an empty array (which means no attributes allowed).
* Otherwise (if granted before via `.grant()`) this will default
* to `["*"]` (which means all attributes allowed.)
*
* @throws {AccessControlError}
* If the access instance to be committed has any invalid data.
*
* @returns {Access}
* Self instance of `Access` so that you can chain and define
* another access instance to be committed.
*/
Access.prototype.deleteAny = function (resource, attributes) {
return this._prepareAndCommit('delete', 'any', resource, attributes);
};
/**
* Alias of `deleteAny`
* @private
*/
Access.prototype.delete = function (resource, attributes) {
return this.deleteAny(resource, attributes);
};
/**
* @param {String} action [description]
* @param {String|Array<String>} resource [description]
* @param {String|Array<String>} attributes [description]
* @returns {Access}
* Self instance of `Access`.
*/
Access.prototype.do = function (action, attributes) {
var segments = action.split(':');
return this._prepareAndCommit(segments[1], segments[2], segments[0], attributes);
};
// -------------------------------
// PRIVATE METHODS
// -------------------------------
/**
* @private
* @param {String} action [description]
* @param {String} possession [description]
* @param {String|Array<String>} resource [description]
* @param {String|Array<String>} attributes [description]
* @returns {Access}
* Self instance of `Access`.
*/
Access.prototype._prepareAndCommit = function (action, possession, resource, attributes) {
this._.action = action;
this._.possession = possession;
if (resource)
this._.resource = resource;
if (this._.denied) {
this._.attributes = [];
}
else {
// if omitted and not denied, all attributes are allowed
this._.attributes = attributes ? utils_1.utils.toStringArray(attributes) : ['*'];
}
utils_1.utils.commitToGrants(this._grants, this._, false);
// important: reset attributes for chained methods
this._.attributes = undefined;
return this;
};
return Access;
}());
exports.Access = Access;