tellform/public/lib/angular-permission/dist/angular-permission.js
2015-07-14 13:45:54 -07:00

233 lines
8.2 KiB
JavaScript

/**
* angular-permission
* Route permission and access control as simple as it can get
* @version v0.3.0 - 2015-06-15
* @link http://www.rafaelvidaurre.com
* @author Rafael Vidaurre <narzerus@gmail.com>
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
(function () {
'use strict';
angular.module('permission', ['ui.router'])
.run(['$rootScope', 'Permission', '$state', '$q',
function ($rootScope, Permission, $state, $q) {
$rootScope.$on('$stateChangeStart',
function (event, toState, toParams, fromState, fromParams) {
if (toState.$$finishAuthorize) {
return;
}
// If there are permissions set then prevent default and attempt to authorize
var permissions;
if (toState.data && toState.data.permissions) {
permissions = toState.data.permissions;
} else if (toState.permissions) {
/**
* This way of defining permissions will be depracated in v1. Should use
* `data` key instead
*/
console.log('Deprecation Warning: permissions should be set inside the `data` key ');
console.log('Setting permissions for a state outside `data` will be depracated in' +
' version 1');
permissions = toState.permissions;
}
if (permissions) {
event.preventDefault();
toState = angular.extend({'$$finishAuthorize': true}, toState);
if ($rootScope.$broadcast('$stateChangePermissionStart', toState, toParams).defaultPrevented) {
return;
}
Permission.authorize(permissions, toParams).then(function () {
// If authorized, use call state.go without triggering the event.
// Then trigger $stateChangeSuccess manually to resume the rest of the process
// Note: This is a pseudo-hacky fix which should be fixed in future ui-router versions
if (!$rootScope.$broadcast('$stateChangeStart', toState, toParams, fromState, fromParams).defaultPrevented) {
$rootScope.$broadcast('$stateChangePermissionAccepted', toState, toParams);
$state.go(toState.name, toParams, {notify: false}).then(function() {
$rootScope
.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});
}
}, function () {
if (!$rootScope.$broadcast('$stateChangeStart', toState, toParams, fromState, fromParams).defaultPrevented) {
$rootScope.$broadcast('$stateChangePermissionDenied', toState, toParams);
var redirectTo = permissions.redirectTo;
var result;
if (angular.isFunction(redirectTo)) {
redirectTo = redirectTo();
$q.when(redirectTo).then(function (newState) {
if (newState) {
$state.go(newState, toParams);
}
});
} else {
if (redirectTo) {
$state.go(redirectTo, toParams);
}
}
}
});
}
});
}]);
}());
(function () {
'use strict';
angular.module('permission')
.provider('Permission', function () {
var roleValidationConfig = {};
var validateRoleDefinitionParams = function (roleName, validationFunction) {
if (!angular.isString(roleName)) {
throw new Error('Role name must be a string');
}
if (!angular.isFunction(validationFunction)) {
throw new Error('Validation function not provided correctly');
}
};
this.defineRole = function (roleName, validationFunction) {
/**
This method is only available in config-time, and cannot access services, as they are
not yet injected anywere which makes this kinda useless.
Should remove if we cannot find a use for it.
**/
validateRoleDefinitionParams(roleName, validationFunction);
roleValidationConfig[roleName] = validationFunction;
return this;
};
this.$get = ['$q', function ($q) {
var Permission = {
_promiseify: function (value) {
/**
Converts a value into a promise, if the value is truthy it resolves it, otherwise
it rejects it
**/
if (value && angular.isFunction(value.then)) {
return value;
}
var deferred = $q.defer();
if (value) {
deferred.resolve();
} else {
deferred.reject();
}
return deferred.promise;
},
_validateRoleMap: function (roleMap) {
if (typeof(roleMap) !== 'object' || roleMap instanceof Array) {
throw new Error('Role map has to be an object');
}
if (roleMap.only === undefined && roleMap.except === undefined) {
throw new Error('Either "only" or "except" keys must me defined');
}
if (roleMap.only) {
if (!(roleMap.only instanceof Array)) {
throw new Error('Array of roles expected');
}
} else if (roleMap.except) {
if (!(roleMap.except instanceof Array)) {
throw new Error('Array of roles expected');
}
}
},
_findMatchingRole: function (rolesArray, toParams) {
var roles = angular.copy(rolesArray);
var deferred = $q.defer();
var currentRole = roles.shift();
// If no roles left to validate reject promise
if (!currentRole) {
deferred.reject();
return deferred.promise;
}
// Validate role definition exists
if (!angular.isFunction(Permission.roleValidations[currentRole])) {
throw new Error('undefined role or invalid role validation');
}
var validatingRole = Permission.roleValidations[currentRole](toParams);
validatingRole = Permission._promiseify(validatingRole);
validatingRole.then(function () {
deferred.resolve();
}, function () {
Permission._findMatchingRole(roles, toParams).then(function () {
deferred.resolve();
}, function () {
deferred.reject();
});
});
return deferred.promise;
},
defineRole: function (roleName, validationFunction) {
/**
Service-available version of defineRole, the callback passed here lives in the
scope where it is defined and therefore can interact with other modules
**/
validateRoleDefinitionParams(roleName, validationFunction);
roleValidationConfig[roleName] = validationFunction;
return Permission;
},
resolveIfMatch: function (rolesArray, toParams) {
var roles = angular.copy(rolesArray);
var deferred = $q.defer();
Permission._findMatchingRole(roles, toParams).then(function () {
// Found role match
deferred.resolve();
}, function () {
// No match
deferred.reject();
});
return deferred.promise;
},
rejectIfMatch: function (roles, toParams) {
var deferred = $q.defer();
Permission._findMatchingRole(roles, toParams).then(function () {
// Role found
deferred.reject();
}, function () {
// Role not found
deferred.resolve();
});
return deferred.promise;
},
roleValidations: roleValidationConfig,
authorize: function (roleMap, toParams) {
// Validate input
Permission._validateRoleMap(roleMap);
var authorizing;
if (roleMap.only) {
authorizing = Permission.resolveIfMatch(roleMap.only, toParams);
} else {
authorizing = Permission.rejectIfMatch(roleMap.except, toParams);
}
return authorizing;
}
};
return Permission;
}];
});
}());