uxess
Version:
Generic authorization module for role-based handling of ui elements
852 lines (749 loc) • 19.5 kB
JavaScript
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs', [
'ngAnimate'
]);
})();
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs').service('uxsAccessHandler', [
'uxsAUTH_TYPES',
'uxsAuthTypeHandler',
'uxsPermitHandler',
'uxsWildcardHandler',
UxsAccessHandler
]);
/**
* @function
* @public
*
* @description
* Handle access and authorization
*
* @param {Object.<string, string>} uxsAUTH_TYPES Available auth types (DI)
* @param {Object.<string, Function>} uxsAuthTypeHandler Factory to handle auth types (DI)
* @param {Object.<string, Function>} uxsPermitHandler Factory to handle permits (DI)
* @param {Object.<string, Function>} uxsWildcardHandler Factory to handle wildcard (DI)
*/
function UxsAccessHandler(uxsAUTH_TYPES, uxsAuthTypeHandler, uxsPermitHandler, uxsWildcardHandler) {
/**
* @function
* @public
*
* @description
* Check if any permits are included in `data.permits`of `uxsPermitHandler`
*
* @returns {boolean} Any permit is set
*/
this.hasPermits = function hasPermits() {
var permits = uxsPermitHandler.getPermits();
return permits.length > 0;
};
/**
* @function
* @public
*
* @description
* Check if all passed permits are included in `data.permits` of `uxsPermitHandler`
*
* @param {(Array.<?string> | string)} permits Permits to be searched for
* @returns {boolean} All passed permits are set
*/
this.hasAllPermits = function hasAllPermits(permits) {
var parsedPermits = uxsPermitHandler.parsePermits(permits);
return parsedPermits.every(_comparePermits);
};
/**
* @function
* @public
*
* @description
* Check if any of passed permits is included in `data.permits` of `uxsPermitHandler`
*
* @param {(Array.<?string> | string)} permits Permits to be searched for
* @returns {boolean} Any of passed permits is set
*/
this.hasAnyPermits = function hasAnyPermits(permits) {
var parsedPermits = uxsPermitHandler.parsePermits(permits);
return parsedPermits.some(_comparePermits);
};
/**
* @function
* @public
*
* @description
* Check if none of passed permits is included in `data.permits` of `uxsPermitHandler`
*
* @param {(Array.<?string> | string)} permits Permits to be searched for
* @returns {boolean} None of passed permits is set
*/
this.hasNonePermits = function hasNonePermits(permits) {
return !this.hasAnyPermits(permits);
};
/**
* @function
* @public
*
* @description
* Check if UI element is accessible for user
*
* @param {(Array.<?string> | string)} permits Permits to be searched for
* @param {string} authType Required auth type
* @returns {boolean} UI element is accessible
*/
this.isPermitted = function isPermitted(permits, authType) {
var isVerified = uxsAuthTypeHandler.isAuthType(authType);
var parsedAuthType = uxsAuthTypeHandler.parseAuthType(authType);
var permitInspector = uxsAUTH_TYPES[parsedAuthType];
var wildcard = uxsWildcardHandler.getWildcard();
var result;
if(permits === wildcard) {
result = this.hasPermits();
} else {
result = isVerified && this[permitInspector](permits);
}
return result;
};
/**
* @function
* @private
*
* @description
* Check if element is included in `data.permits` of `uxsPermitHandler`
*
* @param {string} element Element to be searched for
* @returns {boolean} Element is included
*/
function _comparePermits(element) {
return uxsPermitHandler.getPermits().indexOf(element) !== -1;
}
}
})();
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs').provider('uxsAuthTypeHandler', [
UxsAuthTypeHandler
]);
/**
* @function
* @public
*
* @description
* Handle various authorization types
*/
function UxsAuthTypeHandler() {
/**
* @type{string}
* @private
*
* @description
* Provided default auth type
*/
var _providedDefaultAuthType;
/**
* @function
* @public
*
* @description
* Set `providedDefaultAuthType`
*
* @param {string} authType Auth type to be provided
*/
this.setDefaultAuthType = function setDefaultAuthType(authType) {
_providedDefaultAuthType = authType;
};
/**
* @function
* @public
*
* @description
* Public service to handle various authorization types
*
* @param {Object.<string, string>} uxsAUTH_TYPES Available auth types (DI)
* @returns {Object} Public interface
*/
this.$get = function (uxsAUTH_TYPES) {
/**
* @type {Object}
* @public
*
* @description
* Public service interface
*/
var service = {};
/**
* @type {Object}
* @private
*
* @description
* Store private variables in a centrally manner
*/
var _data = {
defaultAuthType: 'any'
};
/**
* @function
* @public
* @this service
*
* @description
* Set `_data.defaultAuthType`
*
* @param {string} authType Auth type to be set
*/
service.setDefaultAuthType = function setDefaultAuthType(authType) {
var isVerified = this.isAuthType(authType);
var parsedAuthType;
if (isVerified) {
parsedAuthType = this.parseAuthType(authType);
_data.defaultAuthType = parsedAuthType;
}
};
/**
* @function
* @public
* @this service
*
* @description
* Set `_data.defaultAuthType`
*
* @returns {string} `_data.defaultAuthType`
*/
service.getDefaultAuthType = function getDefaultAuthType() {
return _data.defaultAuthType;
};
/**
* @function
* @public
* @this service
*
* @description
* Check if passed auth type is valid
*
* @param {string} authType Auth type to be checked
* @returns {boolean} Auth type is valid
*/
service.isAuthType = function isAuthType(authType) {
var parsedAuthType = this.parseAuthType(authType);
var authTypeKeys = Object.keys(uxsAUTH_TYPES);
return authTypeKeys.indexOf(parsedAuthType) !== -1;
};
/**
* @function
* @public
* @this service
*
* @description
* Parse passed auth type
*
* @param {string} authType Auth type to be parsed
* @returns {string} Parsed auth type
*/
service.parseAuthType = function parseAuthType(authType) {
var parsedAuthType;
if (angular.isString(authType)) {
parsedAuthType = angular.lowercase(authType).trim();
}
return parsedAuthType || _data.defaultAuthType;
};
/**
* @function
* @private
*
* @description
* Parse provided permits if available
*/
function _parseProvidedDefaultAuthType() {
if(_providedDefaultAuthType) {
service.setDefaultAuthType(_providedDefaultAuthType);
}
}
_parseProvidedDefaultAuthType();
return service;
};
}
})();
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs').constant('uxsAUTH_TYPES', {
'any': 'hasAnyPermits',
'all': 'hasAllPermits',
'none': 'hasNonePermits'
});
})();
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs').provider('uxsPermitHandler', [
UxsPermitHandler
]);
/**
* @function
* @public
*
* @description
* Handle permits and share data
*/
function UxsPermitHandler() {
/**
* @type{(Array.<?string> | string)}
* @private
*
* @description
* Provided permits
*/
var _providedPermits;
/**
* @function
* @public
*
* @description
* Set `providedPermits`
*
* @param {(Array.<?string> | string)} permits Permits to be provided
*/
this.setPermits = function setPermits(permits) {
_providedPermits = permits;
};
/**
* @function
* @public
*
* @description
* Public service to handle permits and share data
*
* @param {Object} $rootScope Access to root scope (DI)
* @returns {Object} Public interface
*/
this.$get = function($rootScope) {
/**
* @type {Object}
* @public
*
* @description
* Public service interface
*/
var service = {};
/**
* @type {Object}
* @private
*
* @description
* Store private variables in a centrally manner
*/
var _data = {
permits: []
};
/**
* @function
* @public
* @this service
*
* @description
* Parse permits to list of trimmed and transformed ones
*
* @param {(Array.<?string> | string)} permits Permits to be parsed
* @returns {Array.<?string>} List of permits
*/
service.parsePermits = function parsePermits(permits) {
var parsedPermits;
var splitPermits;
if (permits && angular.isString(permits)) {
splitPermits = permits.split(',');
} else {
splitPermits = permits;
}
if (splitPermits && angular.isArray(splitPermits) ) {
parsedPermits = _parsePermitList(splitPermits);
} else {
parsedPermits = [];
}
return parsedPermits;
};
/**
* @function
* @public
* @this service
*
* @description
* Get `_data.permits`
*
* @returns {Array.<?string>} `_data.permits`
*/
service.getPermits = function getPermits() {
return _data.permits;
};
/**
* @function
* @public
* @this service
*
* @description
* Set `_data.permits`
*
* @fires `uxsPermitsChanged`
*
* @param {(Array.<?string> | string)} permits Permits to be set
*/
service.setPermits = function setPermits(permits) {
_data.permits = this.parsePermits(permits);
$rootScope.$broadcast('uxsPermitsChanged');
};
/**
* @function
* @private
* @this service
*
* @description
* Parse list of permits by trimming and transforming
*
* @param {Array.<?string>} permits Permits to be parsed
* @returns {Array.<?string>} List of permits
*/
function _parsePermitList(permits) {
return permits.map(function (permit) {
var trimmedPermits;
var parsedPermits;
try {
trimmedPermits = permit.toString().trim();
parsedPermits = angular.lowercase(trimmedPermits);
} catch(error) {
parsedPermits = '';
}
return parsedPermits;
});
}
/**
* @function
* @private
*
* @description
* Parse provided permits if available
*/
function _parseProvidedPermits() {
if(_providedPermits) {
service.setPermits(_providedPermits);
}
}
_parseProvidedPermits();
return service;
};
}
})();
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs').provider('uxsWildcardHandler', [
UxsWildcardHandler
]);
/**
* @function
* @public
*
* @description
* Handle wildcard and share data
*/
function UxsWildcardHandler() {
/**
* @type{string}
* @private
*
* @description
* Provided wildcard
*/
var _providedWildcard;
/**
* @function
* @public
*
* @description
* Set `providedPermits`
*
* @param {string} wildcard Wildcard symbol
*/
this.setWildcard = function setWildcard(wildcard) {
_providedWildcard = wildcard;
};
/**
* @function
* @public
*
* @description
* Public service to handle wildcard and share data
*
* @returns {Object} Public interface
*/
this.$get = function() {
/**
* @type {Object}
* @public
*
* @description
* Public service interface
*/
var service = {};
/**
* @type {Object}
* @private
*
* @description
* Store private variables in a centrally manner
*/
var _data = {
wildcard: null,
defaultWildcard: '*'
};
/**
* @function
* @public
* @this service
*
* @description
* Parse wildcard to string
*
* @param {string} wildcard Wildcard to be parsed
* @returns {string} Parsed wildcard
*/
service.parseWildcard = function parseWildcard(wildcard) {
var parsedWildcard;
try {
parsedWildcard = wildcard.toString();
} catch(error) {
parsedWildcard = _data.defaultWildcard;
}
return parsedWildcard;
};
/**
* @function
* @public
* @this service
*
* @description
* Get `_data.wildcard`
*
* @returns {Array.<?string>} `_data.permits`
*/
service.getWildcard = function getWildcard() {
return _data.wildcard || _data.defaultWildcard;
};
/**
* @function
* @public
* @this service
*
* @description
* Set `_data.wildcard`
*
* @param {string} wildcard Wildcard to be set
*/
service.setWildcard = function setWildcard(wildcard) {
_data.wildcard = this.parseWildcard(wildcard);
};
/**
* @function
* @private
*
* @description
* Parse provided wildcard if available
*/
function _parseProvidedWildcard() {
if(_providedWildcard) {
service.setWildcard(_providedWildcard);
}
}
_parseProvidedWildcard();
return service;
};
}
})();
/*!
* @author Felix Heck <hi@whoTheHeck.de>
* @version 0.1.2
* @copyright Felix Heck 2016
* @license MIT
*/
;(function() {
'use strict';
angular.module('uxs').directive('uxsIf', [
'$animate',
'$interpolate',
'$parse',
'uxsAccessHandler',
uxsIf
]);
/**
* @function
* @public
*
* @description
* Handle UI elements based on permits
*
* @param {Object} $animate Service to animate UI elements (DI)
* @param {Object} $interpolate Service to convert expressions (DI)
* @param {Object} $parse Service to convert expression contents (DI)
* @param {Object.<string, Function>} uxsAccessHandler Factory to handle authorization (DI)
* @returns {Object} Grant access to private scope
*/
function uxsIf($animate, $interpolate, $parse, uxsAccessHandler) {
/**
* @function
* @private
*
* @description
* Extract the attribute's value by parsing or taking over
*
* @param {Object} scope Current scope context
* @param {string} attr Attribute's value
* @returns {Array.<?string> | string} Extracted attribute's value
*/
function getAttributeValue(scope, attr) {
var hasExpression = isExpression(attr);
var parseFn = $parse;
var extractedAttr;
if(hasExpression) {
parseFn = $interpolate;
}
try {
extractedAttr = parseFn(attr)(scope);
} catch(error) {
extractedAttr = attr;
}
return extractedAttr || attr;
}
/**
* @function
* @private
*
* @description
* Build trailing comment node
*
* @param {Array.<?string>} attrs List of attributes declared on this element
* @returns {Object} Comment node
*/
function getCommentNode(attrs) {
return document.createComment(' end uxsIf: ' + attrs.uxsIf + ' ');
}
/**
* @function
* @private
*
* @description
* Check if the user meets the required permits and the auth type
*
* @param {Object} scope Current scope context
* @param {Object} attrs The HTML elements attributes
* @returns {boolean} If the user credentials are acceptable
*/
function isAuthorized(scope, attrs) {
var permits = getAttributeValue(scope, attrs.uxsIf);
var authType = getAttributeValue(scope, attrs.uxsType);
return uxsAccessHandler.isPermitted(permits, authType);
}
/**
* @function
* @private
*
* @description
* Check if the attribute value matches the expression pattern
*
* @param {string} attr Attribute value to be checked for
* @returns {boolean} Matches the exprression pattern
*/
function isExpression(attr) {
return attr && attr.search(/{{2}.*}{2}/) !== -1;
}
/**
* @function
* @public
*
* @description
* Handle DOM manipulation
*
* @listens `uxsPermitsChanged`
*
* @param {Object} scope Scope to be used for registering event handler
* @param {Object} element The element where the directive is to be used
* @param {Array.<?string>} attrs List of attributes declared on this element
* @param {Function} ctrl Directive's required controller instance
* @param {Function} transclude Transclude linking function
*/
function link(scope, element, attrs, ctrl, transclude) {
var isAccessible;
var childScope;
var cloneReference;
/**
* @function
* @private
*
* @description
* Show or hide element based on credentials
*/
function checkVisibility() {
isAccessible = isAuthorized(scope, attrs);
if (isAccessible) {
if(!childScope) {
transclude(function (clone, newScope) {
childScope = newScope;
clone.push(getCommentNode(attrs));
cloneReference = clone;
$animate.enter(clone, element.parent(), element);
});
}
} else {
if(childScope) {
childScope.$destroy();
childScope = null;
}
if(cloneReference) {
$animate.leave(cloneReference);
cloneReference = null;
}
}
}
checkVisibility();
scope.$on('uxsPermitsChanged', checkVisibility);
}
return {
multiElement: true,
transclude: 'element',
priority: 800,
terminal: true,
restrict: 'A',
link: link
};
}
})();