3 * Route permission and access control as simple as it can get
4 * @version v2.3.6 - 2016-04-11
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();
115 // Overwrite notify option to broadcast it later
116 TransitionProperties.options = angular.extend({}, TransitionProperties.options, {notify: false});
119 .go(TransitionProperties.toState.name, TransitionProperties.toParams, TransitionProperties.options)
121 TransitionEvents.broadcastStateChangeSuccess();
126 * Handles redirection for unauthorized access
130 * @param rejectedPermission {String} Rejected access right
131 * @param statePermissionMap {permission.StatePermissionMap} State permission map
133 function handleUnauthorizedState(rejectedPermission, statePermissionMap) {
134 TransitionEvents.broadcastStateChangePermissionDenied();
137 .resolveRedirectState(rejectedPermission)
138 .then(function (redirect) {
139 $state.go(redirect.state, redirect.params, redirect.options);
145 angular.module('permission', ['ui.router'])
155 * Extends $q implementation by A+ *any* method
157 * @extends {angular.$q}
158 * @memberOf permission
160 * @param $delegate {Object} Angular promise implementation
162 $q.$inject = ['$delegate'];
163 function $q($delegate) {
168 * Implementation of missing $q `any` method that wits for first resolution of provided promise set
171 * @param promises {Array|promise} Single or set of promises
173 * @returns {Promise} Returns a single promise that will be rejected with an array/hash of values,
174 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
175 * If any of the promises is resolved, this resulting promise will be returned
176 * with the same resolution value.
178 function any(promises) {
179 var deferred = $delegate.defer(),
181 results = angular.isArray(promises) ? [] : {};
183 angular.forEach(promises, function (promise, key) {
187 .then(function (value) {
188 deferred.resolve(value);
190 .catch(function (reason) {
191 results[key] = reason;
193 deferred.reject(reason);
199 deferred.reject(results);
202 return deferred.promise;
209 .module('permission')
210 .decorator('$q', $q);
219 * Pre-defined available configurable behaviours of directive `permission`
220 * @name PermissionStrategies
221 * @memberOf permission
226 * permission-except="'MANAGER'"
227 * permission-on-authorized="PermissionStrategies.renderContent"
228 * permission-on-unauthorized="PermissionStrategies.removeContent">
231 * @property enableElement {Function}
232 * @property disableElement {Function}
233 * @property showElement {Function}
234 * @property hideElement {Function}
236 var PermissionStrategies = {
237 enableElement: function ($element) {
238 $element.removeAttr('disabled');
240 disableElement: function ($element) {
241 $element.attr('disabled', 'disabled');
243 showElement: function ($element) {
244 $element.removeClass('ng-hide');
246 hideElement: function ($element) {
247 $element.addClass('ng-hide');
252 .module('permission')
253 .constant('PermissionStrategies', PermissionStrategies);
262 * Helper object used for storing ui-router transition parameters
263 * @name TransitionProperties
264 * @memberOf permission
266 * @type {Object.<String,Object>}
268 * UI-router transition properties:
269 * @property toState {Object} Target state object
270 * @property toParams {Object} Target state params
271 * @property fromState {Object} Source state object
272 * @property fromParams {Object} Source state params
273 * @property options {Object} Transition options
275 var TransitionProperties = {
278 fromState: undefined,
279 fromParams: undefined,
284 .module('permission')
285 .value('TransitionProperties', TransitionProperties);
293 * Service responsible for managing and emitting events
294 * @name TransitionEvents
295 * @memberOf permission
297 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
298 * @param $rootScope {Object} Top-level angular scope
300 TransitionEvents.$inject = ['$rootScope', 'TransitionProperties'];
301 function TransitionEvents($rootScope, TransitionProperties) {
303 this.areStateEventsDefaultPrevented = areStateEventsDefaultPrevented;
304 this.broadcastStateChangePermissionStart = broadcastStateChangePermissionStart;
305 this.broadcastStateChangePermissionAccepted = broadcastStateChangePermissionAccepted;
306 this.broadcastStateChangePermissionDenied = broadcastStateChangePermissionDenied;
307 this.broadcastStateChangeSuccess = broadcastStateChangeSuccess;
310 * Checks if state events are not prevented by default
315 function areStateEventsDefaultPrevented() {
316 return isStateChangePermissionStartDefaultPrevented() || isStateChangeStartDefaultPrevented();
320 * Broadcasts "$stateChangePermissionStart" event from $rootScope
323 function broadcastStateChangePermissionStart() {
324 $rootScope.$broadcast('$stateChangePermissionStart',
325 TransitionProperties.toState, TransitionProperties.toParams,
326 TransitionProperties.options);
330 * Broadcasts "$stateChangePermissionAccepted" event from $rootScope
333 function broadcastStateChangePermissionAccepted() {
334 $rootScope.$broadcast('$stateChangePermissionAccepted',
335 TransitionProperties.toState, TransitionProperties.toParams,
336 TransitionProperties.options);
340 * Broadcasts "$stateChangeSuccess" event from $rootScope
343 function broadcastStateChangeSuccess() {
344 $rootScope.$broadcast('$stateChangeSuccess',
345 TransitionProperties.toState, TransitionProperties.toParams,
346 TransitionProperties.fromState, TransitionProperties.fromParams);
350 * Broadcasts "$tateChangePermissionDenied" event from $rootScope
353 function broadcastStateChangePermissionDenied() {
354 $rootScope.$broadcast('$stateChangePermissionDenied',
355 TransitionProperties.toState, TransitionProperties.toParams,
356 TransitionProperties.options);
360 * Checks if event $stateChangeStart hasn't been disabled by default
366 function isStateChangeStartDefaultPrevented() {
367 return $rootScope.$broadcast('$stateChangeStart',
368 TransitionProperties.toState, TransitionProperties.toParams,
369 TransitionProperties.fromState, TransitionProperties.fromParams,
370 TransitionProperties.options).defaultPrevented;
374 * Checks if event $stateChangePermissionStart hasn't been disabled by default
380 function isStateChangePermissionStartDefaultPrevented() {
381 return $rootScope.$broadcast('$stateChangePermissionStart',
382 TransitionProperties.toState, TransitionProperties.toParams,
383 TransitionProperties.options).defaultPrevented;
388 .module('permission')
389 .service('TransitionEvents', TransitionEvents);
397 * Access rights map factory
398 * @name PermissionMapFactory
400 * @param $q {Object} Angular promise implementation
401 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
402 * @param RoleStore {permission.RoleStore} Role definition storage
403 * @param PermissionStore {permission.PermissionStore} Permission definition storage
405 * @return {permission.PermissionMap}
407 PermissionMapFactory.$inject = ['$q', 'TransitionProperties', 'RoleStore', 'PermissionStore'];
408 function PermissionMapFactory($q, TransitionProperties, RoleStore, PermissionStore) {
410 * Constructs map object instructing authorization service how to handle authorizing
411 * @constructor PermissionMap
412 * @memberOf permission
414 * @param [permissionMap] {Object} Map of permissions provided to authorization service
415 * @param [permissionMap.only] {Array} List of exclusive access right names allowed for authorization
416 * @param [permissionMap.except] {Array} List of exclusive access right names denied for authorization
417 * @param [permissionMap.redirectTo] {String|Function|Object|promise} Handling redirection when rejected
420 function PermissionMap(permissionMap) {
421 // Suppress not defined object errors
422 permissionMap = permissionMap || {};
424 this.only = normalizeMapProperty(permissionMap.only);
425 this.except = normalizeMapProperty(permissionMap.except);
426 this.redirectTo = permissionMap.redirectTo;
430 * Redirects to fallback states when permissions fail
432 * @methodOf permission.PermissionMap
434 * @param rejectedPermissionName {String} Permission name
438 PermissionMap.prototype.resolveRedirectState = function (rejectedPermissionName) {
439 if (angular.isFunction(this.redirectTo)) {
440 return resolveFunctionRedirect(this.redirectTo, rejectedPermissionName);
443 if (angular.isObject(this.redirectTo)) {
444 return resolveObjectRedirect(this.redirectTo, rejectedPermissionName);
447 if (angular.isString(this.redirectTo)) {
449 state: this.redirectTo
453 // If redirectTo state is not defined stay where you are
454 return $q.reject(null);
458 * Resolves weather permissions set for "only" or "except" property are valid
461 * @param property {permissionMap.only|permissionMap.except} "only" or "except" map property
462 * @returns {Array<Promise>}
464 PermissionMap.prototype.resolvePropertyValidity = function (property) {
466 return property.map(function (privilegeName) {
468 if (RoleStore.hasRoleDefinition(privilegeName)) {
469 var role = RoleStore.getRoleDefinition(privilegeName);
470 return role.validateRole();
473 if (PermissionStore.hasPermissionDefinition(privilegeName)) {
474 var permission = PermissionStore.getPermissionDefinition(privilegeName);
475 return permission.validatePermission();
478 return $q.reject(privilegeName);
483 * Handles function based redirection for rejected permissions
485 * @methodOf permission.PermissionMap
486 * @throws {TypeError}
488 * @param redirectFunction {Function} Redirection function
489 * @param permission {String} Rejected permission
493 function resolveFunctionRedirect(redirectFunction, permission) {
495 .when(redirectFunction.call(null, permission))
496 .then(function (redirectState) {
497 if (angular.isString(redirectState)) {
503 if (angular.isObject(redirectState)) {
504 return redirectState;
507 throw new TypeError('When used "redirectTo" as function, returned value must be string or object');
512 * Handles object based redirection for rejected permissions
514 * @throws {ReferenceError}
516 * @param redirectObject {Object} Redirection function
517 * @param permission {String} Rejected permission
521 function resolveObjectRedirect(redirectObject, permission) {
522 if (!angular.isDefined(redirectObject['default'])) {
523 throw new ReferenceError('When used "redirectTo" as object, property "default" must be defined');
526 var redirectState = redirectObject[permission];
528 if (!angular.isDefined(redirectState)) {
529 redirectState = redirectObject['default'];
532 if (angular.isFunction(redirectState)) {
533 return resolveFunctionRedirect(redirectState, permission);
536 if (angular.isObject(redirectState)) {
537 return $q.resolve(redirectState);
540 if (angular.isString(redirectState)) {
548 * Handles extraction of permission map "only" and "except" properties and converts them into array objects
552 * @param property {String|Array|Function|Promise} Permission map property "only" or "except"
554 * @returns {Array<String>} Array of permission "only" or "except" names
556 function normalizeMapProperty(property) {
557 if (angular.isString(property)) {
561 if (angular.isArray(property)) {
565 if (angular.isFunction(property)) {
566 return property.call(null, TransitionProperties);
572 return PermissionMap;
576 .module('permission')
577 .factory('PermissionMap', PermissionMapFactory);
584 * State Access rights map factory
585 * @name StatePermissionMapFactory
587 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
588 * @param PermissionMap {permission.PermissionMap}
590 * @return {permission.StatePermissionMap}
592 StatePermissionMapFactory.$inject = ['TransitionProperties', 'PermissionMap'];
593 function StatePermissionMapFactory(TransitionProperties, PermissionMap) {
595 StatePermissionMap.prototype = new PermissionMap();
596 StatePermissionMap.constructor = StatePermissionMap;
597 StatePermissionMap.prototype.parent = PermissionMap.prototype;
601 * Constructs map object instructing authorization service how to handle authorizing
602 * @constructor StatePermissionMap
603 * @extends PermissionMap
604 * @memberOf permission
606 function StatePermissionMap() {
607 this.parent.constructor.call(this);
609 var toStateObject = TransitionProperties.toState.$$state();
610 var toStatePath = toStateObject.path.slice().reverse();
612 angular.forEach(toStatePath, function (state) {
614 if (state.areSetStatePermissions()) {
615 var permissionMap = new PermissionMap(state.data.permissions);
616 this.extendPermissionMap(permissionMap);
622 * Extends permission map by pushing to it state's permissions
624 * @methodOf permission.StatePermissionMap
626 * @param permissionMap {permission.PermissionMap} Compensated permission map
628 StatePermissionMap.prototype.extendPermissionMap = function (permissionMap) {
629 if (permissionMap.only.length) {
630 this.only = this.only.concat([permissionMap.only]);
632 if (permissionMap.except.length) {
633 this.except = this.except.concat([permissionMap.except]);
635 this.redirectTo = permissionMap.redirectTo;
638 return StatePermissionMap;
642 .module('permission')
643 .factory('StatePermissionMap', StatePermissionMapFactory);
650 * Permission definition factory
651 * @name PermissionFactory
653 * @param $q {Object} Angular promise implementation
654 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
656 * @return {permission.Permission}
658 PermissionFactory.$inject = ['$q', 'TransitionProperties'];
659 function PermissionFactory($q, TransitionProperties) {
661 * Permission definition object constructor
663 * @memberOf permission
665 * @param permissionName {String} Name repressing permission
666 * @param validationFunction {Function} Function used to check if permission is valid
668 function Permission(permissionName, validationFunction) {
669 validateConstructor(permissionName, validationFunction);
671 this.permissionName = permissionName;
672 this.validationFunction = validationFunction;
676 * Checks if permission is still valid
678 * @methodOf permission.Permission
682 Permission.prototype.validatePermission = function () {
683 var validationResult = this.validationFunction.call(null, this.permissionName, TransitionProperties);
685 if (!angular.isFunction(validationResult.then)) {
686 validationResult = wrapInPromise(validationResult, this.permissionName);
689 return validationResult;
693 * Converts a value into a promise, if the value is truthy it resolves it, otherwise it rejects it
697 * @param result {Boolean} Function to be wrapped into promise
698 * @param permissionName {String} Returned value in promise
701 function wrapInPromise(result, permissionName) {
702 var dfd = $q.defer();
705 dfd.resolve(permissionName);
707 dfd.reject(permissionName);
714 * Checks if provided permission has accepted parameter types
717 * @throws {TypeError}
719 function validateConstructor(permissionName, validationFunction) {
720 if (!angular.isString(permissionName)) {
721 throw new TypeError('Parameter "permissionName" name must be String');
723 if (!angular.isFunction(validationFunction)) {
724 throw new TypeError('Parameter "validationFunction" must be Function');
732 .module('permission')
733 .factory('Permission', PermissionFactory);
741 * Role definition factory
744 * @param $q {Object} Angular promise implementation
745 * @param PermissionStore {permission.PermissionStore} Permission definition storage
746 * @param TransitionProperties {permission.TransitionProperties} Helper storing ui-router transition parameters
748 * @return {permission.Role}
750 RoleFactory.$inject = ['$q', 'PermissionStore', 'TransitionProperties'];
751 function RoleFactory($q, PermissionStore, TransitionProperties) {
753 * Role definition constructor
755 * @memberOf permission
757 * @param roleName {String} Name representing role
758 * @param permissionNames {Array} List of permission names representing role
759 * @param [validationFunction] {Function} Optional function used to validate if permissions are still valid
761 function Role(roleName, permissionNames, validationFunction) {
762 validateConstructor(roleName, permissionNames, validationFunction);
763 this.roleName = roleName;
764 this.permissionNames = permissionNames || [];
765 this.validationFunction = validationFunction;
767 if (validationFunction) {
768 PermissionStore.defineManyPermissions(permissionNames, validationFunction);
773 * Checks if role is still valid
775 * @methodOf permission.Role
777 * @returns {Promise} $q.promise object
779 Role.prototype.validateRole = function () {
780 // When permission set is provided check each of them
781 if (this.permissionNames.length) {
782 var promises = this.permissionNames.map(function (permissionName) {
783 if (PermissionStore.hasPermissionDefinition(permissionName)) {
784 var permission = PermissionStore.getPermissionDefinition(permissionName);
785 var validationResult = permission.validatePermission();
787 if (!angular.isFunction(validationResult.then)) {
788 validationResult = wrapInPromise(validationResult);
791 return validationResult;
797 return $q.all(promises);
800 // If not call validation function manually
801 var validationResult = this.validationFunction.call(null, this.roleName, TransitionProperties);
802 if (!angular.isFunction(validationResult.then)) {
803 validationResult = wrapInPromise(validationResult, this.roleName);
806 return $q.resolve(validationResult);
810 * Converts a value into a promise, if the value is truthy it resolves it, otherwise it rejects it
814 * @param result {Boolean} Function to be wrapped into promise
815 * @param [roleName] {String} Returned value in promise
819 function wrapInPromise(result, roleName) {
820 var dfd = $q.defer();
823 dfd.resolve(roleName);
825 dfd.reject(roleName);
832 * Checks if provided permission has accepted parameter types
835 * @throws {TypeError}
837 function validateConstructor(roleName, permissionNames, validationFunction) {
838 if (!angular.isString(roleName)) {
839 throw new TypeError('Parameter "roleName" name must be String');
842 if (!angular.isArray(permissionNames)) {
843 throw new TypeError('Parameter "permissionNames" must be Array');
846 if (!permissionNames.length && !angular.isFunction(validationFunction)) {
847 throw new TypeError('Parameter "validationFunction" must be provided for empty "permissionNames" array');
855 .module('permission')
856 .factory('Role', RoleFactory);
864 * Permission definition storage
865 * @name PermissionStore
866 * @memberOf permission
868 * @param Permission {permission.PermissionFactory} Permission definition factory
870 PermissionStore.$inject = ['Permission'];
871 function PermissionStore(Permission) {
873 * @property permissionStore
877 var permissionStore = {};
879 this.definePermission = definePermission;
880 this.defineManyPermissions = defineManyPermissions;
881 this.removePermissionDefinition = removePermissionDefinition;
882 this.hasPermissionDefinition = hasPermissionDefinition;
883 this.getPermissionDefinition = getPermissionDefinition;
884 this.getStore = getStore;
885 this.clearStore = clearStore;
888 * Allows to define permission on application configuration
891 * @param permissionName {String} Name of defined permission
892 * @param validationFunction {Function} Function used to validate if permission is valid
894 function definePermission(permissionName, validationFunction) {
895 var permission = new Permission(permissionName, validationFunction);
896 permissionStore[permissionName] = permission;
900 * Allows to define set of permissionNames with shared validation function on application configuration
902 * @throws {TypeError}
904 * @param permissionNames {Array<String>} Set of permission names
905 * @param validationFunction {Function} Function used to validate if permission is valid
907 function defineManyPermissions(permissionNames, validationFunction) {
908 if (!angular.isArray(permissionNames)) {
909 throw new TypeError('Parameter "permissionNames" name must be Array');
912 angular.forEach(permissionNames, function (permissionName) {
913 definePermission(permissionName, validationFunction);
921 * @param permissionName {String} Name of defined permission
923 function removePermissionDefinition(permissionName) {
924 delete permissionStore[permissionName];
928 * Checks if permission exists
931 * @param permissionName {String} Name of defined permission
934 function hasPermissionDefinition(permissionName) {
935 return angular.isDefined(permissionStore[permissionName]);
939 * Returns permission by it's name
942 * @returns {permission.Permission} Permissions definition object
944 function getPermissionDefinition(permissionName) {
945 return permissionStore[permissionName];
949 * Returns all permissions
952 * @returns {Object} Permissions collection
954 function getStore() {
955 return permissionStore;
959 * Removes all permissions
962 function clearStore() {
963 permissionStore = {};
968 .module('permission')
969 .service('PermissionStore', PermissionStore);
976 * Role definition storage
978 * @memberOf permission
980 * @param Role {permission.Role|Function} Role definition constructor
982 RoleStore.$inject = ['Role'];
983 function RoleStore(Role) {
986 this.defineRole = defineRole;
987 this.getRoleDefinition = getRoleDefinition;
988 this.hasRoleDefinition = hasRoleDefinition;
989 this.removeRoleDefinition = removeRoleDefinition;
990 this.getStore = getStore;
991 this.clearStore = clearStore;
994 * Allows to define role
997 * @param roleName {String} Name of defined role
998 * @param permissions {Array<String>} Set of permission names
999 * @param [validationFunction] {Function} Function used to validate if permissions in role are valid
1001 function defineRole(roleName, permissions, validationFunction) {
1002 roleStore[roleName] = new Role(roleName, permissions, validationFunction);
1006 * Deletes role from store
1009 * @param roleName {String} Name of defined permission
1011 function removeRoleDefinition(roleName) {
1012 delete roleStore[roleName];
1016 * Checks if role is defined in store
1019 * @param roleName {String} Name of role
1020 * @returns {Boolean}
1022 function hasRoleDefinition(roleName) {
1023 return angular.isDefined(roleStore[roleName]);
1027 * Returns role definition object by it's name
1030 * @returns {permission.Role} Role definition object
1032 function getRoleDefinition(roleName) {
1033 return roleStore[roleName];
1037 * Returns all role definitions
1040 * @returns {Object} Defined roles collection
1042 function getStore() {
1047 * Removes all role definitions
1050 function clearStore() {
1056 .module('permission')
1057 .service('RoleStore', RoleStore);
1064 * Handles authorization based on provided permissions/roles.
1065 * @name permissionDirective
1066 * @memberOf permission
1068 * Directive accepts single or combined attributes `permission-only` and `permission-except` that checks on
1069 * DOM rendering if permissions/roles are met. Attributes can be passed either as String, Array or variable from
1070 * parent scope. Directive also will watch for changes if applied and automatically update the view.
1074 * permission-only="'USER'">
1077 * permission-only="['USER','ADMIN']"
1078 * permission-except="'MANAGER'">
1081 * By default directive will show/hide elements if provided permissions matches.
1082 * You can override this behaviour by passing `permission-on-authorized` and `permission-on-unauthorized`
1083 * attributes that will pass to your function `$element` as argument that you can freely manipulate your DOM
1086 * Important! Function should be as references - `vm.disableElement` not `vm.disableElement()` to be able to
1087 * accept passed $element reference from inside of permissionDirective
1091 * permission-only="['USER','ADMIN']"
1092 * permission-on-authorized="PermissionStrategies.disableElement"
1093 * permission-on-unauthorized="PermissionStrategies.enableElement">
1096 * @param $log {Object} Logging service
1097 * @param Authorization {permission.Authorization} Authorization service
1098 * @param PermissionMap {permission.PermissionMap} Map of state access rights
1099 * @param PermissionStrategies {permission.PermissionStrategies} Set of pre-defined directive behaviours
1101 * @returns {Object} Directive instance
1103 permissionDirective.$inject = ['$log', 'Authorization', 'PermissionMap', 'PermissionStrategies'];
1104 function permissionDirective($log, Authorization, PermissionMap, PermissionStrategies) {
1108 only: '=?permissionOnly',
1109 except: '=?permissionExcept',
1110 onAuthorized: '&?permissionOnAuthorized',
1111 onUnauthorized: '&?permissionOnUnauthorized',
1112 // Observing attribute `only` and `except` will be removed with version 2.4.0+
1113 deprecatedOnly: '=only',
1114 deprecatedExcept: '=except'
1116 controllerAs: 'permission',
1117 controller: ['$scope', '$element', function ($scope, $element) {
1118 var permission = this;
1120 if (angular.isDefined(permission.deprecatedOnly) || angular.isDefined(permission.deprecatedExcept)) {
1121 $log.warn('Attributes "only" and "except" are deprecated since 2.2.0+ and their support ' +
1122 'will be removed from 2.4.0. Use scoped "permission-only" and "permission-except" instead.');
1126 * Observing attribute `only` and `except` will be removed with version 2.4.0+
1128 $scope.$watchGroup(['permission.only', 'permission.except',
1129 'permission.deprecatedOnly', 'permission.deprecatedExcept'],
1132 var permissionMap = new PermissionMap({
1133 only: permission.only || permission.deprecatedOnly,
1134 except: permission.except || permission.deprecatedExcept
1138 .authorize(permissionMap)
1140 onAuthorizedAccess();
1142 .catch(function () {
1143 onUnauthorizedAccess();
1146 onUnauthorizedAccess();
1147 $log.error(e.message);
1152 * Calls `onAuthorized` function if provided or show element
1155 function onAuthorizedAccess() {
1156 if (angular.isFunction(permission.onAuthorized)) {
1157 permission.onAuthorized()($element);
1159 PermissionStrategies.showElement($element);
1164 * Calls `onUnauthorized` function if provided or hide element
1167 function onUnauthorizedAccess() {
1168 if (angular.isFunction(permission.onUnauthorized)) {
1169 permission.onUnauthorized()($element);
1171 PermissionStrategies.hideElement($element);
1179 .module('permission')
1180 .directive('permission', permissionDirective);
1189 * Service responsible for handling view based authorization
1190 * @name Authorization
1191 * @memberOf permission
1193 * @param $q {Object} Angular promise implementation
1195 Authorization.$inject = ['$q'];
1196 function Authorization($q) {
1198 this.authorize = authorize;
1201 * Handles authorization based on provided permissions map
1204 * @param permissionsMap {permission.PermissionMap} Map of permission names
1206 * @returns {promise} $q.promise object
1208 function authorize(permissionsMap) {
1210 return authorizePermissionMap(permissionsMap);
1214 * Checks authorization for simple view based access
1218 * @param map {permission.PermissionMap} Access rights map
1220 * @returns {promise} $q.promise object
1222 function authorizePermissionMap(map) {
1223 var deferred = $q.defer();
1225 resolveExceptPrivilegeMap(deferred, map);
1227 return deferred.promise;
1231 * Resolves flat set of "except" privileges
1235 * @param deferred {Object} Promise defer
1236 * @param map {permission.PermissionMap} Access rights map
1238 * @returns {Promise} $q.promise object
1240 function resolveExceptPrivilegeMap(deferred, map) {
1241 var exceptPromises = map.resolvePropertyValidity(map.except);
1243 $q.any(exceptPromises)
1244 .then(function (rejectedPermissions) {
1245 deferred.reject(rejectedPermissions);
1247 .catch(function () {
1248 resolveOnlyPermissionMap(deferred, map);
1253 * Resolves flat set of "only" privileges
1257 * @param deferred {Object} Promise defer
1258 * @param map {permission.PermissionMap} Access rights map
1260 function resolveOnlyPermissionMap(deferred, map) {
1261 if (!map.only.length) {
1266 var onlyPromises = map.resolvePropertyValidity(map.only);
1267 $q.any(onlyPromises)
1268 .then(function (resolvedPermissions) {
1269 deferred.resolve(resolvedPermissions);
1271 .catch(function (rejectedPermission) {
1272 deferred.reject(rejectedPermission);
1278 .module('permission')
1279 .service('Authorization', Authorization);
1288 * Service responsible for handling state based authorization
1289 * @name StateAuthorization
1290 * @memberOf permission
1292 * @param $q {Object} Angular promise implementation
1294 StateAuthorization.$inject = ['$q'];
1295 function StateAuthorization($q) {
1297 this.authorize = authorize;
1300 * Handles state authorization
1301 * @method {permission.StatePermissionMap}
1302 * @param statePermissionMap
1306 function authorize(statePermissionMap) {
1307 return authorizeStatePermissionMap(statePermissionMap);
1311 * Checks authorization for complex state inheritance
1315 * @param map {permission.StatePermissionMap} State access rights map
1317 * @returns {promise} $q.promise object
1319 function authorizeStatePermissionMap(map) {
1320 var deferred = $q.defer();
1322 resolveExceptStatePermissionMap(deferred, map);
1324 return deferred.promise;
1328 * Resolves compensated set of "except" privileges
1332 * @param deferred {Object} Promise defer
1333 * @param map {permission.StatePermissionMap} State access rights map
1335 function resolveExceptStatePermissionMap(deferred, map) {
1336 var exceptPromises = resolveStatePermissionMap(map.except, map);
1338 $q.all(exceptPromises)
1339 .then(function (rejectedPermissions) {
1340 deferred.reject(rejectedPermissions);
1342 .catch(function () {
1343 resolveOnlyStatePermissionMap(deferred, map);
1348 * Resolves compensated set of "only" privileges
1352 * @param deferred {Object} Promise defer
1353 * @param map {permission.StatePermissionMap} State access rights map
1355 function resolveOnlyStatePermissionMap(deferred, map) {
1356 if (!map.only.length) {
1361 var onlyPromises = resolveStatePermissionMap(map.only, map);
1363 $q.all(onlyPromises)
1364 .then(function (resolvedPermissions) {
1365 deferred.resolve(resolvedPermissions);
1367 .catch(function (rejectedPermission) {
1368 deferred.reject(rejectedPermission);
1373 * Performs iteration over list of privileges looking for matches
1377 * @param privilegesNames {Array} Array of sets of access rights
1378 * @param map {permission.StatePermissionMap} State access rights map
1380 * @returns {Array<Promise>} Promise collection
1382 function resolveStatePermissionMap(privilegesNames, map) {
1383 if (!privilegesNames.length) {
1384 return [$q.reject()];
1387 return privilegesNames.map(function (statePrivileges) {
1388 var resolvedStatePrivileges = map.resolvePropertyValidity(statePrivileges);
1389 return $q.any(resolvedStatePrivileges);
1395 .module('permission')
1396 .service('StateAuthorization', StateAuthorization);