3 * Route permission and access control as simple as it can get
4 * @version v2.3.7 - 2016-04-28
5 * @link https://github.com/Narzerus/angular-permission
6 * @author Rafael Vidaurre <narzerus@gmail.com> (http://www.rafaelvidaurre.com), Blazej Krysiak <blazej.krysiak@gmail.com>
7 * @license MIT License, http://www.opensource.org/licenses/MIT
14 * @namespace permission
17 config.$inject = ['$stateProvider'];
18 run.$inject = ['$rootScope', '$location', '$state', 'TransitionProperties', 'TransitionEvents', 'StateAuthorization', 'StatePermissionMap'];
19 function config($stateProvider) {
21 * This decorator is required to access full state object instead of it's configuration
22 * when trying to obtain full toState state object not it's configuration
23 * Can be removed when implemented https://github.com/angular-ui/ui-router/issues/13.
25 $stateProvider.decorator('parent', function (state, parentFn) {
26 state.self.$$state = function () {
30 state.self.areSetStatePermissions = function () {
31 return angular.isDefined(state.data) && angular.isDefined(state.data.permissions);
34 return parentFn(state);
38 function run($rootScope, $location, $state, TransitionProperties, TransitionEvents, StateAuthorization, StatePermissionMap) {
40 * State transition interceptor
42 $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, options) {
44 if (!isAuthorizationFinished()) {
45 event.preventDefault();
47 setStateAuthorizationStatus(true);
48 setTransitionProperties();
50 if (!TransitionEvents.areStateEventsDefaultPrevented()) {
51 TransitionEvents.broadcastStateChangePermissionStart();
53 var statePermissionMap = new StatePermissionMap();
56 .authorize(statePermissionMap)
58 handleAuthorizedState();
60 .catch(function (rejectedPermission) {
61 handleUnauthorizedState(rejectedPermission, statePermissionMap);
63 .finally(function () {
64 setStateAuthorizationStatus(false);
70 * Updates values of `TransitionProperties` holder object
74 function setTransitionProperties() {
75 TransitionProperties.toState = toState;
76 TransitionProperties.toParams = toParams;
77 TransitionProperties.fromState = fromState;
78 TransitionProperties.fromParams = fromParams;
79 TransitionProperties.options = options;
83 * Sets internal state `$$finishedAuthorization` variable to prevent looping
88 * @param status {boolean} When true authorization has been already preceded
90 function setStateAuthorizationStatus(status) {
91 angular.extend(toState, {'$$isAuthorizationFinished': status});
95 * Checks if state has been already checked for authorization
101 function isAuthorizationFinished() {
102 return toState.$$isAuthorizationFinished;
106 * Handles redirection for authorized access
110 function handleAuthorizedState() {
112 TransitionEvents.broadcastStateChangePermissionAccepted();
114 // Overwrite notify option to broadcast it later
115 TransitionProperties.options = angular.extend({}, TransitionProperties.options, {notify: false});
119 TransitionProperties.toState.name,
120 TransitionProperties.toParams,
121 angular.extend({}, TransitionProperties.options, {location: 'replace'})
124 TransitionEvents.broadcastStateChangeSuccess();
129 * Handles redirection for unauthorized access
133 * @param rejectedPermission {String} Rejected access right
134 * @param statePermissionMap {permission.StatePermissionMap} State permission map
136 function handleUnauthorizedState(rejectedPermission, statePermissionMap) {
137 TransitionEvents.broadcastStateChangePermissionDenied();
140 .resolveRedirectState(rejectedPermission)
141 .then(function (redirect) {
142 $state.go(redirect.state, redirect.params, redirect.options);
148 angular.module('permission', ['ui.router'])
158 * Extends $q implementation by A+ *any* method
160 * @extends {angular.$q}
161 * @memberOf permission
163 * @param $delegate {Object} Angular promise implementation
165 $q.$inject = ['$delegate'];
166 function $q($delegate) {
171 * Implementation of missing $q `any` method that wits for first resolution of provided promise set
174 * @param promises {Array|promise} Single or set of promises
176 * @returns {Promise} Returns a single promise that will be rejected with an array/hash of values,
177 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
178 * If any of the promises is resolved, this resulting promise will be returned
179 * with the same resolution value.
181 function any(promises) {
182 var deferred = $delegate.defer(),
184 results = angular.isArray(promises) ? [] : {};
186 angular.forEach(promises, function (promise, key) {
190 .then(function (value) {
191 deferred.resolve(value);
193 .catch(function (reason) {
194 results[key] = reason;
196 deferred.reject(reason);
202 deferred.reject(results);
205 return deferred.promise;
212 .module('permission')
213 .decorator('$q', $q);
222 * Pre-defined available configurable behaviours of directive `permission`
223 * @name PermissionStrategies
224 * @memberOf permission
229 * permission-except="'MANAGER'"
230 * permission-on-authorized="PermissionStrategies.renderContent"
231 * permission-on-unauthorized="PermissionStrategies.removeContent">
234 * @property enableElement {Function}
235 * @property disableElement {Function}
236 * @property showElement {Function}
237 * @property hideElement {Function}
239 var PermissionStrategies = {
240 enableElement: function ($element) {
241 $element.removeAttr('disabled');
243 disableElement: function ($element) {
244 $element.attr('disabled', 'disabled');
246 showElement: function ($element) {
247 $element.removeClass('ng-hide');
249 hideElement: function ($element) {
250 $element.addClass('ng-hide');
255 .module('permission')
256 .constant('PermissionStrategies', PermissionStrategies);
265 * Helper object used for storing ui-router transition parameters
266 * @name TransitionProperties
267 * @memberOf permission
269 * @type {Object.<String,Object>}
271 * UI-router transition properties:
272 * @property toState {Object} Target state object
273 * @property toParams {Object} Target state params
274 * @property fromState {Object} Source state object
275 * @property fromParams {Object} Source state params
276 * @property options {Object} Transition options
278 var TransitionProperties = {
281 fromState: undefined,
282 fromParams: undefined,
287 .module('permission')
288 .value('TransitionProperties', TransitionProperties);
296 * Service responsible for managing and emitting events
297 * @name TransitionEvents
298 * @memberOf permission
300 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
301 * @param $rootScope {Object} Top-level angular scope
303 TransitionEvents.$inject = ['$rootScope', 'TransitionProperties'];
304 function TransitionEvents($rootScope, TransitionProperties) {
306 this.areStateEventsDefaultPrevented = areStateEventsDefaultPrevented;
307 this.broadcastStateChangePermissionStart = broadcastStateChangePermissionStart;
308 this.broadcastStateChangePermissionAccepted = broadcastStateChangePermissionAccepted;
309 this.broadcastStateChangePermissionDenied = broadcastStateChangePermissionDenied;
310 this.broadcastStateChangeSuccess = broadcastStateChangeSuccess;
313 * Checks if state events are not prevented by default
318 function areStateEventsDefaultPrevented() {
319 return isStateChangePermissionStartDefaultPrevented() || isStateChangeStartDefaultPrevented();
323 * Broadcasts "$stateChangePermissionStart" event from $rootScope
326 function broadcastStateChangePermissionStart() {
327 $rootScope.$broadcast('$stateChangePermissionStart',
328 TransitionProperties.toState, TransitionProperties.toParams,
329 TransitionProperties.options);
333 * Broadcasts "$stateChangePermissionAccepted" event from $rootScope
336 function broadcastStateChangePermissionAccepted() {
337 $rootScope.$broadcast('$stateChangePermissionAccepted',
338 TransitionProperties.toState, TransitionProperties.toParams,
339 TransitionProperties.options);
343 * Broadcasts "$stateChangeSuccess" event from $rootScope
346 function broadcastStateChangeSuccess() {
347 $rootScope.$broadcast('$stateChangeSuccess',
348 TransitionProperties.toState, TransitionProperties.toParams,
349 TransitionProperties.fromState, TransitionProperties.fromParams);
353 * Broadcasts "$tateChangePermissionDenied" event from $rootScope
356 function broadcastStateChangePermissionDenied() {
357 $rootScope.$broadcast('$stateChangePermissionDenied',
358 TransitionProperties.toState, TransitionProperties.toParams,
359 TransitionProperties.options);
363 * Checks if event $stateChangeStart hasn't been disabled by default
369 function isStateChangeStartDefaultPrevented() {
370 return $rootScope.$broadcast('$stateChangeStart',
371 TransitionProperties.toState, TransitionProperties.toParams,
372 TransitionProperties.fromState, TransitionProperties.fromParams,
373 TransitionProperties.options).defaultPrevented;
377 * Checks if event $stateChangePermissionStart hasn't been disabled by default
383 function isStateChangePermissionStartDefaultPrevented() {
384 return $rootScope.$broadcast('$stateChangePermissionStart',
385 TransitionProperties.toState, TransitionProperties.toParams,
386 TransitionProperties.options).defaultPrevented;
391 .module('permission')
392 .service('TransitionEvents', TransitionEvents);
400 * Access rights map factory
401 * @name PermissionMapFactory
403 * @param $q {Object} Angular promise implementation
404 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
405 * @param RoleStore {permission.RoleStore} Role definition storage
406 * @param PermissionStore {permission.PermissionStore} Permission definition storage
408 * @return {permission.PermissionMap}
410 PermissionMapFactory.$inject = ['$q', 'TransitionProperties', 'RoleStore', 'PermissionStore'];
411 function PermissionMapFactory($q, TransitionProperties, RoleStore, PermissionStore) {
413 * Constructs map object instructing authorization service how to handle authorizing
414 * @constructor PermissionMap
415 * @memberOf permission
417 * @param [permissionMap] {Object} Map of permissions provided to authorization service
418 * @param [permissionMap.only] {Array} List of exclusive access right names allowed for authorization
419 * @param [permissionMap.except] {Array} List of exclusive access right names denied for authorization
420 * @param [permissionMap.redirectTo] {String|Function|Object|promise} Handling redirection when rejected
423 function PermissionMap(permissionMap) {
424 // Suppress not defined object errors
425 permissionMap = permissionMap || {};
427 this.only = normalizeMapProperty(permissionMap.only);
428 this.except = normalizeMapProperty(permissionMap.except);
429 this.redirectTo = permissionMap.redirectTo;
433 * Redirects to fallback states when permissions fail
435 * @methodOf permission.PermissionMap
437 * @param rejectedPermissionName {String} Permission name
441 PermissionMap.prototype.resolveRedirectState = function (rejectedPermissionName) {
442 if (angular.isFunction(this.redirectTo)) {
443 return resolveFunctionRedirect(this.redirectTo, rejectedPermissionName);
446 if (angular.isObject(this.redirectTo)) {
447 return resolveObjectRedirect(this.redirectTo, rejectedPermissionName);
450 if (angular.isString(this.redirectTo)) {
452 state: this.redirectTo
456 // If redirectTo state is not defined stay where you are
457 return $q.reject(null);
461 * Resolves weather permissions set for "only" or "except" property are valid
464 * @param property {permissionMap.only|permissionMap.except} "only" or "except" map property
465 * @returns {Array<Promise>}
467 PermissionMap.prototype.resolvePropertyValidity = function (property) {
469 return property.map(function (privilegeName) {
471 if (RoleStore.hasRoleDefinition(privilegeName)) {
472 var role = RoleStore.getRoleDefinition(privilegeName);
473 return role.validateRole();
476 if (PermissionStore.hasPermissionDefinition(privilegeName)) {
477 var permission = PermissionStore.getPermissionDefinition(privilegeName);
478 return permission.validatePermission();
481 return $q.reject(privilegeName);
486 * Handles function based redirection for rejected permissions
488 * @methodOf permission.PermissionMap
489 * @throws {TypeError}
491 * @param redirectFunction {Function} Redirection function
492 * @param permission {String} Rejected permission
496 function resolveFunctionRedirect(redirectFunction, permission) {
498 .when(redirectFunction.call(null, permission))
499 .then(function (redirectState) {
500 if (angular.isString(redirectState)) {
506 if (angular.isObject(redirectState)) {
507 return redirectState;
510 throw new TypeError('When used "redirectTo" as function, returned value must be string or object');
515 * Handles object based redirection for rejected permissions
517 * @throws {ReferenceError}
519 * @param redirectObject {Object} Redirection function
520 * @param permission {String} Rejected permission
524 function resolveObjectRedirect(redirectObject, permission) {
525 if (!angular.isDefined(redirectObject['default'])) {
526 throw new ReferenceError('When used "redirectTo" as object, property "default" must be defined');
529 var redirectState = redirectObject[permission];
531 if (!angular.isDefined(redirectState)) {
532 redirectState = redirectObject['default'];
535 if (angular.isFunction(redirectState)) {
536 return resolveFunctionRedirect(redirectState, permission);
539 if (angular.isObject(redirectState)) {
540 return $q.resolve(redirectState);
543 if (angular.isString(redirectState)) {
551 * Handles extraction of permission map "only" and "except" properties and converts them into array objects
555 * @param property {String|Array|Function|Promise} Permission map property "only" or "except"
557 * @returns {Array<String>} Array of permission "only" or "except" names
559 function normalizeMapProperty(property) {
560 if (angular.isString(property)) {
564 if (angular.isArray(property)) {
568 if (angular.isFunction(property)) {
569 return property.call(null, TransitionProperties);
575 return PermissionMap;
579 .module('permission')
580 .factory('PermissionMap', PermissionMapFactory);
587 * State Access rights map factory
588 * @name StatePermissionMapFactory
590 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
591 * @param PermissionMap {permission.PermissionMap}
593 * @return {permission.StatePermissionMap}
595 StatePermissionMapFactory.$inject = ['TransitionProperties', 'PermissionMap'];
596 function StatePermissionMapFactory(TransitionProperties, PermissionMap) {
598 StatePermissionMap.prototype = new PermissionMap();
599 StatePermissionMap.constructor = StatePermissionMap;
600 StatePermissionMap.prototype.parent = PermissionMap.prototype;
604 * Constructs map object instructing authorization service how to handle authorizing
605 * @constructor StatePermissionMap
606 * @extends PermissionMap
607 * @memberOf permission
609 function StatePermissionMap() {
610 this.parent.constructor.call(this);
612 var toStateObject = TransitionProperties.toState.$$state();
613 var toStatePath = toStateObject.path.slice().reverse();
615 angular.forEach(toStatePath, function (state) {
617 if (state.areSetStatePermissions()) {
618 var permissionMap = new PermissionMap(state.data.permissions);
619 this.extendPermissionMap(permissionMap);
625 * Extends permission map by pushing to it state's permissions
627 * @methodOf permission.StatePermissionMap
629 * @param permissionMap {permission.PermissionMap} Compensated permission map
631 StatePermissionMap.prototype.extendPermissionMap = function (permissionMap) {
632 if (permissionMap.only.length) {
633 this.only = this.only.concat([permissionMap.only]);
635 if (permissionMap.except.length) {
636 this.except = this.except.concat([permissionMap.except]);
638 this.redirectTo = permissionMap.redirectTo;
641 return StatePermissionMap;
645 .module('permission')
646 .factory('StatePermissionMap', StatePermissionMapFactory);
653 * Permission definition factory
654 * @name PermissionFactory
656 * @param $q {Object} Angular promise implementation
657 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
659 * @return {permission.Permission}
661 PermissionFactory.$inject = ['$q', 'TransitionProperties'];
662 function PermissionFactory($q, TransitionProperties) {
664 * Permission definition object constructor
666 * @memberOf permission
668 * @param permissionName {String} Name repressing permission
669 * @param validationFunction {Function} Function used to check if permission is valid
671 function Permission(permissionName, validationFunction) {
672 validateConstructor(permissionName, validationFunction);
674 this.permissionName = permissionName;
675 this.validationFunction = validationFunction;
679 * Checks if permission is still valid
681 * @methodOf permission.Permission
685 Permission.prototype.validatePermission = function () {
686 var validationResult = this.validationFunction.call(null, this.permissionName, TransitionProperties);
688 if (!angular.isFunction(validationResult.then)) {
689 validationResult = wrapInPromise(validationResult, this.permissionName);
692 return validationResult;
696 * Converts a value into a promise, if the value is truthy it resolves it, otherwise it rejects it
700 * @param result {Boolean} Function to be wrapped into promise
701 * @param permissionName {String} Returned value in promise
704 function wrapInPromise(result, permissionName) {
705 var dfd = $q.defer();
708 dfd.resolve(permissionName);
710 dfd.reject(permissionName);
717 * Checks if provided permission has accepted parameter types
720 * @throws {TypeError}
722 function validateConstructor(permissionName, validationFunction) {
723 if (!angular.isString(permissionName)) {
724 throw new TypeError('Parameter "permissionName" name must be String');
726 if (!angular.isFunction(validationFunction)) {
727 throw new TypeError('Parameter "validationFunction" must be Function');
735 .module('permission')
736 .factory('Permission', PermissionFactory);
744 * Role definition factory
747 * @param $q {Object} Angular promise implementation
748 * @param PermissionStore {permission.PermissionStore} Permission definition storage
749 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
751 * @return {permission.Role}
753 RoleFactory.$inject = ['$q', 'PermissionStore', 'TransitionProperties'];
754 function RoleFactory($q, PermissionStore, TransitionProperties) {
756 * Role definition constructor
758 * @memberOf permission
760 * @param roleName {String} Name representing role
761 * @param permissionNames {Array} List of permission names representing role
762 * @param [validationFunction] {Function} Optional function used to validate if permissions are still valid
764 function Role(roleName, permissionNames, validationFunction) {
765 validateConstructor(roleName, permissionNames, validationFunction);
766 this.roleName = roleName;
767 this.permissionNames = permissionNames || [];
768 this.validationFunction = validationFunction;
770 if (validationFunction) {
771 PermissionStore.defineManyPermissions(permissionNames, validationFunction);
776 * Checks if role is still valid
778 * @methodOf permission.Role
780 * @returns {Promise} $q.promise object
782 Role.prototype.validateRole = function () {
783 // When permission set is provided check each of them
784 if (this.permissionNames.length) {
785 var promises = this.permissionNames.map(function (permissionName) {
786 if (PermissionStore.hasPermissionDefinition(permissionName)) {
787 var permission = PermissionStore.getPermissionDefinition(permissionName);
788 var validationResult = permission.validatePermission();
790 if (!angular.isFunction(validationResult.then)) {
791 validationResult = wrapInPromise(validationResult);
794 return validationResult;
800 return $q.all(promises);
803 // If not call validation function manually
804 var validationResult = this.validationFunction.call(null, this.roleName, TransitionProperties);
805 if (!angular.isFunction(validationResult.then)) {
806 validationResult = wrapInPromise(validationResult, this.roleName);
809 return $q.resolve(validationResult);
813 * Converts a value into a promise, if the value is truthy it resolves it, otherwise it rejects it
817 * @param result {Boolean} Function to be wrapped into promise
818 * @param [roleName] {String} Returned value in promise
822 function wrapInPromise(result, roleName) {
823 var dfd = $q.defer();
826 dfd.resolve(roleName);
828 dfd.reject(roleName);
835 * Checks if provided permission has accepted parameter types
838 * @throws {TypeError}
840 function validateConstructor(roleName, permissionNames, validationFunction) {
841 if (!angular.isString(roleName)) {
842 throw new TypeError('Parameter "roleName" name must be String');
845 if (!angular.isArray(permissionNames)) {
846 throw new TypeError('Parameter "permissionNames" must be Array');
849 if (!permissionNames.length && !angular.isFunction(validationFunction)) {
850 throw new TypeError('Parameter "validationFunction" must be provided for empty "permissionNames" array');
858 .module('permission')
859 .factory('Role', RoleFactory);
867 * Permission definition storage
868 * @name PermissionStore
869 * @memberOf permission
871 * @param Permission {permission.PermissionFactory} Permission definition factory
873 PermissionStore.$inject = ['Permission'];
874 function PermissionStore(Permission) {
876 * @property permissionStore
880 var permissionStore = {};
882 this.definePermission = definePermission;
883 this.defineManyPermissions = defineManyPermissions;
884 this.removePermissionDefinition = removePermissionDefinition;
885 this.hasPermissionDefinition = hasPermissionDefinition;
886 this.getPermissionDefinition = getPermissionDefinition;
887 this.getStore = getStore;
888 this.clearStore = clearStore;
891 * Allows to define permission on application configuration
894 * @param permissionName {String} Name of defined permission
895 * @param validationFunction {Function} Function used to validate if permission is valid
897 function definePermission(permissionName, validationFunction) {
898 var permission = new Permission(permissionName, validationFunction);
899 permissionStore[permissionName] = permission;
903 * Allows to define set of permissionNames with shared validation function on application configuration
905 * @throws {TypeError}
907 * @param permissionNames {Array<String>} Set of permission names
908 * @param validationFunction {Function} Function used to validate if permission is valid
910 function defineManyPermissions(permissionNames, validationFunction) {
911 if (!angular.isArray(permissionNames)) {
912 throw new TypeError('Parameter "permissionNames" name must be Array');
915 angular.forEach(permissionNames, function (permissionName) {
916 definePermission(permissionName, validationFunction);
924 * @param permissionName {String} Name of defined permission
926 function removePermissionDefinition(permissionName) {
927 delete permissionStore[permissionName];
931 * Checks if permission exists
934 * @param permissionName {String} Name of defined permission
937 function hasPermissionDefinition(permissionName) {
938 return angular.isDefined(permissionStore[permissionName]);
942 * Returns permission by it's name
945 * @returns {permission.Permission} Permissions definition object
947 function getPermissionDefinition(permissionName) {
948 return permissionStore[permissionName];
952 * Returns all permissions
955 * @returns {Object} Permissions collection
957 function getStore() {
958 return permissionStore;
962 * Removes all permissions
965 function clearStore() {
966 permissionStore = {};
971 .module('permission')
972 .service('PermissionStore', PermissionStore);
979 * Role definition storage
981 * @memberOf permission
983 * @param Role {permission.Role|Function} Role definition constructor
985 RoleStore.$inject = ['Role'];
986 function RoleStore(Role) {
989 this.defineRole = defineRole;
990 this.getRoleDefinition = getRoleDefinition;
991 this.hasRoleDefinition = hasRoleDefinition;
992 this.removeRoleDefinition = removeRoleDefinition;
993 this.getStore = getStore;
994 this.clearStore = clearStore;
997 * Allows to define role
1000 * @param roleName {String} Name of defined role
1001 * @param permissions {Array<String>} Set of permission names
1002 * @param [validationFunction] {Function} Function used to validate if permissions in role are valid
1004 function defineRole(roleName, permissions, validationFunction) {
1005 roleStore[roleName] = new Role(roleName, permissions, validationFunction);
1009 * Deletes role from store
1012 * @param roleName {String} Name of defined permission
1014 function removeRoleDefinition(roleName) {
1015 delete roleStore[roleName];
1019 * Checks if role is defined in store
1022 * @param roleName {String} Name of role
1023 * @returns {Boolean}
1025 function hasRoleDefinition(roleName) {
1026 return angular.isDefined(roleStore[roleName]);
1030 * Returns role definition object by it's name
1033 * @returns {permission.Role} Role definition object
1035 function getRoleDefinition(roleName) {
1036 return roleStore[roleName];
1040 * Returns all role definitions
1043 * @returns {Object} Defined roles collection
1045 function getStore() {
1050 * Removes all role definitions
1053 function clearStore() {
1059 .module('permission')
1060 .service('RoleStore', RoleStore);
1067 * Handles authorization based on provided permissions/roles.
1068 * @name permissionDirective
1069 * @memberOf permission
1071 * Directive accepts single or combined attributes `permission-only` and `permission-except` that checks on
1072 * DOM rendering if permissions/roles are met. Attributes can be passed either as String, Array or variable from
1073 * parent scope. Directive also will watch for changes if applied and automatically update the view.
1077 * permission-only="'USER'">
1080 * permission-only="['USER','ADMIN']"
1081 * permission-except="'MANAGER'">
1084 * By default directive will show/hide elements if provided permissions matches.
1085 * You can override this behaviour by passing `permission-on-authorized` and `permission-on-unauthorized`
1086 * attributes that will pass to your function `$element` as argument that you can freely manipulate your DOM
1089 * Important! Function should be as references - `vm.disableElement` not `vm.disableElement()` to be able to
1090 * accept passed $element reference from inside of permissionDirective
1094 * permission-only="['USER','ADMIN']"
1095 * permission-on-authorized="PermissionStrategies.disableElement"
1096 * permission-on-unauthorized="PermissionStrategies.enableElement">
1099 * @param $log {Object} Logging service
1100 * @param Authorization {permission.Authorization} Authorization service
1101 * @param PermissionMap {permission.PermissionMap} Map of state access rights
1102 * @param PermissionStrategies {permission.PermissionStrategies} Set of pre-defined directive behaviours
1104 * @returns {Object} Directive instance
1106 permissionDirective.$inject = ['$log', 'Authorization', 'PermissionMap', 'PermissionStrategies'];
1107 function permissionDirective($log, Authorization, PermissionMap, PermissionStrategies) {
1111 only: '=?permissionOnly',
1112 except: '=?permissionExcept',
1113 onAuthorized: '&?permissionOnAuthorized',
1114 onUnauthorized: '&?permissionOnUnauthorized',
1115 // Observing attribute `only` and `except` will be removed with version 2.4.0+
1116 deprecatedOnly: '=only',
1117 deprecatedExcept: '=except'
1119 controllerAs: 'permission',
1120 controller: ['$scope', '$element', function ($scope, $element) {
1121 var permission = this;
1123 if (angular.isDefined(permission.deprecatedOnly) || angular.isDefined(permission.deprecatedExcept)) {
1124 $log.warn('Attributes "only" and "except" are deprecated since 2.2.0+ and their support ' +
1125 'will be removed from 2.4.0. Use scoped "permission-only" and "permission-except" instead.');
1129 * Observing attribute `only` and `except` will be removed with version 2.4.0+
1131 $scope.$watchGroup(['permission.only', 'permission.except',
1132 'permission.deprecatedOnly', 'permission.deprecatedExcept'],
1135 var permissionMap = new PermissionMap({
1136 only: permission.only || permission.deprecatedOnly,
1137 except: permission.except || permission.deprecatedExcept
1141 .authorize(permissionMap)
1143 onAuthorizedAccess();
1145 .catch(function () {
1146 onUnauthorizedAccess();
1149 onUnauthorizedAccess();
1150 $log.error(e.message);
1155 * Calls `onAuthorized` function if provided or show element
1158 function onAuthorizedAccess() {
1159 if (angular.isFunction(permission.onAuthorized)) {
1160 permission.onAuthorized()($element);
1162 PermissionStrategies.showElement($element);
1167 * Calls `onUnauthorized` function if provided or hide element
1170 function onUnauthorizedAccess() {
1171 if (angular.isFunction(permission.onUnauthorized)) {
1172 permission.onUnauthorized()($element);
1174 PermissionStrategies.hideElement($element);
1182 .module('permission')
1183 .directive('permission', permissionDirective);
1192 * Service responsible for handling view based authorization
1193 * @name Authorization
1194 * @memberOf permission
1196 * @param $q {Object} Angular promise implementation
1198 Authorization.$inject = ['$q'];
1199 function Authorization($q) {
1201 this.authorize = authorize;
1204 * Handles authorization based on provided permissions map
1207 * @param permissionsMap {permission.PermissionMap} Map of permission names
1209 * @returns {promise} $q.promise object
1211 function authorize(permissionsMap) {
1213 return authorizePermissionMap(permissionsMap);
1217 * Checks authorization for simple view based access
1221 * @param map {permission.PermissionMap} Access rights map
1223 * @returns {promise} $q.promise object
1225 function authorizePermissionMap(map) {
1226 var deferred = $q.defer();
1228 resolveExceptPrivilegeMap(deferred, map);
1230 return deferred.promise;
1234 * Resolves flat set of "except" privileges
1238 * @param deferred {Object} Promise defer
1239 * @param map {permission.PermissionMap} Access rights map
1241 * @returns {Promise} $q.promise object
1243 function resolveExceptPrivilegeMap(deferred, map) {
1244 var exceptPromises = map.resolvePropertyValidity(map.except);
1246 $q.any(exceptPromises)
1247 .then(function (rejectedPermissions) {
1248 deferred.reject(rejectedPermissions);
1250 .catch(function () {
1251 resolveOnlyPermissionMap(deferred, map);
1256 * Resolves flat set of "only" privileges
1260 * @param deferred {Object} Promise defer
1261 * @param map {permission.PermissionMap} Access rights map
1263 function resolveOnlyPermissionMap(deferred, map) {
1264 if (!map.only.length) {
1269 var onlyPromises = map.resolvePropertyValidity(map.only);
1270 $q.any(onlyPromises)
1271 .then(function (resolvedPermissions) {
1272 deferred.resolve(resolvedPermissions);
1274 .catch(function (rejectedPermission) {
1275 deferred.reject(rejectedPermission);
1281 .module('permission')
1282 .service('Authorization', Authorization);
1291 * Service responsible for handling state based authorization
1292 * @name StateAuthorization
1293 * @memberOf permission
1295 * @param $q {Object} Angular promise implementation
1297 StateAuthorization.$inject = ['$q'];
1298 function StateAuthorization($q) {
1300 this.authorize = authorize;
1303 * Handles state authorization
1304 * @method {permission.StatePermissionMap}
1305 * @param statePermissionMap
1309 function authorize(statePermissionMap) {
1310 return authorizeStatePermissionMap(statePermissionMap);
1314 * Checks authorization for complex state inheritance
1318 * @param map {permission.StatePermissionMap} State access rights map
1320 * @returns {promise} $q.promise object
1322 function authorizeStatePermissionMap(map) {
1323 var deferred = $q.defer();
1325 resolveExceptStatePermissionMap(deferred, map);
1327 return deferred.promise;
1331 * Resolves compensated set of "except" privileges
1335 * @param deferred {Object} Promise defer
1336 * @param map {permission.StatePermissionMap} State access rights map
1338 function resolveExceptStatePermissionMap(deferred, map) {
1339 var exceptPromises = resolveStatePermissionMap(map.except, map);
1341 $q.all(exceptPromises)
1342 .then(function (rejectedPermissions) {
1343 deferred.reject(rejectedPermissions);
1345 .catch(function () {
1346 resolveOnlyStatePermissionMap(deferred, map);
1351 * Resolves compensated set of "only" privileges
1355 * @param deferred {Object} Promise defer
1356 * @param map {permission.StatePermissionMap} State access rights map
1358 function resolveOnlyStatePermissionMap(deferred, map) {
1359 if (!map.only.length) {
1364 var onlyPromises = resolveStatePermissionMap(map.only, map);
1366 $q.all(onlyPromises)
1367 .then(function (resolvedPermissions) {
1368 deferred.resolve(resolvedPermissions);
1370 .catch(function (rejectedPermission) {
1371 deferred.reject(rejectedPermission);
1376 * Performs iteration over list of privileges looking for matches
1380 * @param privilegesNames {Array} Array of sets of access rights
1381 * @param map {permission.StatePermissionMap} State access rights map
1383 * @returns {Array<Promise>} Promise collection
1385 function resolveStatePermissionMap(privilegesNames, map) {
1386 if (!privilegesNames.length) {
1387 return [$q.reject()];
1390 return privilegesNames.map(function (statePrivileges) {
1391 var resolvedStatePrivileges = map.resolvePropertyValidity(statePrivileges);
1392 return $q.any(resolvedStatePrivileges);
1398 .module('permission')
1399 .service('StateAuthorization', StateAuthorization);