Skip to content

Commit d2f98e1

Browse files
committed
#116 - Redirection per role
Closes #116.
1 parent 28964fe commit d2f98e1

File tree

4 files changed

+366
-92
lines changed

4 files changed

+366
-92
lines changed

.jshintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"eqeqeq": true,
99
"immed": true,
1010
"indent": 2,
11-
"latedef": true,
11+
"latedef": false,
1212
"newcap": true,
1313
"noarg": true,
1414
"quotmark": "single",

src/permission.mdl.js

+150-40
Original file line numberDiff line numberDiff line change
@@ -6,58 +6,168 @@
66
permission.run(['$rootScope', 'Permission', '$state', '$q', function ($rootScope, Permission, $state, $q) {
77
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
88

9-
if (toState.$$finishAuthorize || !toState.data || !toState.data.permissions) {
10-
return;
9+
if (areSetStatePermissions()) {
10+
setStateAuthorizationStatus(true);
11+
event.preventDefault();
12+
13+
if (!areStateEventsDefaultPrevented()) {
14+
var permissions = toState.data.permissions;
15+
authorizeForState(permissions);
16+
}
17+
} else {
18+
setStateAuthorizationStatus(false);
19+
}
20+
21+
/**
22+
* Checks if state is qualified to be permission based verified
23+
*
24+
* @returns {boolean}
25+
*/
26+
function areSetStatePermissions() {
27+
return !toState.$$isAuthorizationFinished && toState.data && toState.data.permissions;
1128
}
1229

13-
var permissions = toState.data.permissions;
30+
/**
31+
* Sets internal state `$$finishedAuthorization` variable to prevent looping
32+
*
33+
* @param status {boolean} When true authorization has been already preceded
34+
*/
35+
function setStateAuthorizationStatus(status) {
36+
toState = angular.extend({'$$isAuthorizationFinished': status}, toState);
37+
}
1438

15-
event.preventDefault();
16-
toState = angular.extend({'$$finishAuthorize': true}, toState);
1739

18-
if ($rootScope.$broadcast('$stateChangePermissionStart', toState, toParams).defaultPrevented) {
19-
return;
40+
/**
41+
* Checks if state events are not prevented by default
42+
*
43+
* @returns {boolean}
44+
*/
45+
function areStateEventsDefaultPrevented() {
46+
return isStateChangePermissionStartDefaultPrevented() || isStateChangeStartDefaultPrevented();
2047
}
2148

22-
Permission
23-
.authorize(permissions, toParams)
24-
.then(function () {
25-
// If authorized, use call state.go without triggering the event.
26-
// Then trigger $stateChangeSuccess manually to resume the rest of the process
27-
// Note: This is a pseudo-hacky fix which should be fixed in future ui-router versions
28-
if (!$rootScope.$broadcast('$stateChangeStart', toState, toParams, fromState, fromParams).defaultPrevented) {
49+
/**
50+
* Handles state authorization
51+
*
52+
* @param permissions {Object} Map of "only" or "except" permission names
53+
*/
54+
function authorizeForState(permissions) {
55+
Permission
56+
.authorize(permissions, toParams)
57+
.then(function () {
2958
$rootScope.$broadcast('$stateChangePermissionAccepted', toState, toParams);
30-
31-
$state
32-
.go(toState.name, toParams, {notify: false})
33-
.then(function () {
34-
$rootScope
35-
.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
36-
});
37-
}
38-
})
39-
.catch(function () {
40-
if (!$rootScope.$broadcast('$stateChangeStart', toState, toParams, fromState, fromParams).defaultPrevented) {
59+
goToState(toState.name);
60+
})
61+
.catch(function (rejectedPermission) {
4162
$rootScope.$broadcast('$stateChangePermissionDenied', toState, toParams);
63+
redirectToState(permissions.redirectTo, rejectedPermission);
64+
});
65+
}
4266

43-
var redirectTo = permissions.redirectTo;
67+
/**
68+
* Redirects to states when permissions are met
69+
*
70+
* If authorized, use call state.go without triggering the event.
71+
* Then trigger $stateChangeSuccess manually to resume the rest of the process
72+
* Note: This is a pseudo-hacky fix which should be fixed in future ui-router versions
73+
*/
74+
function goToState(name) {
75+
$state
76+
.go(name, toParams, {notify: false})
77+
.then(function () {
78+
$rootScope
79+
.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
80+
});
81+
}
82+
83+
/**
84+
* Redirects to fallback states when permissions fail
85+
*
86+
* @param redirectTo {Object|Function|String} Redirection configuration
87+
* @param permission {String} Permission name
88+
*/
89+
function redirectToState(redirectTo, permission) {
90+
if (angular.isFunction(redirectTo)) {
91+
handleFunctionRedirect(redirectTo, permission);
92+
}
4493

45-
if (angular.isFunction(redirectTo)) {
46-
redirectTo = redirectTo();
94+
if (angular.isObject(redirectTo)) {
95+
handleObjectRedirect(redirectTo, permission);
96+
}
4797

48-
$q.when(redirectTo).then(function (newState) {
49-
if (newState) {
50-
$state.go(newState, toParams);
51-
}
52-
});
98+
if (angular.isString(redirectTo)) {
99+
handleStringRedirect(redirectTo);
100+
}
101+
}
53102

54-
} else {
55-
if (redirectTo) {
56-
$state.go(redirectTo, toParams);
57-
}
103+
/**
104+
* Handles function based redirection for rejected permissions
105+
*
106+
* @param redirectFunction {Function} Redirection function
107+
* @param permission {String} Rejected permission
108+
*/
109+
function handleFunctionRedirect(redirectFunction, permission) {
110+
$q.when(redirectFunction.call(null, permission))
111+
.then(function (redirectState) {
112+
if (!angular.isString(redirectState)) {
113+
throw new TypeError('When used "redirectTo" as function, returned value must be string with state name');
58114
}
59-
}
60-
});
115+
handleStringRedirect(redirectState);
116+
});
117+
}
118+
119+
/**
120+
* Handles object based redirection for rejected permissions
121+
*
122+
* @param redirectObject {Object} Redirection function
123+
* @param permission {String} Rejected permission
124+
*/
125+
function handleObjectRedirect(redirectObject, permission) {
126+
if (!angular.isDefined(redirectObject['default'])) {
127+
throw new ReferenceError('When used "redirectTo" as object, property "default" must be defined');
128+
}
129+
130+
var redirectState = redirectObject[permission];
131+
132+
if (!angular.isDefined(redirectState)) {
133+
redirectState = redirectObject['default'];
134+
}
135+
136+
if (angular.isFunction(redirectState)) {
137+
handleFunctionRedirect(redirectState, permission);
138+
}
139+
140+
if (angular.isString(redirectState)){
141+
handleStringRedirect(redirectState);
142+
}
143+
}
144+
145+
/**
146+
* Handles string based redirection for rejected permissions
147+
*
148+
* @param state {String} State to which app should be redirected
149+
*/
150+
function handleStringRedirect(state){
151+
$state.go(state, toParams);
152+
}
153+
154+
/**
155+
* Checks if event $stateChangeStart hasn't been disabled by default
156+
*
157+
* @returns {boolean}
158+
*/
159+
function isStateChangeStartDefaultPrevented() {
160+
return $rootScope.$broadcast('$stateChangeStart', toState, toParams, fromState, fromParams).defaultPrevented;
161+
}
162+
163+
/**
164+
* Checks if event $stateChangePermissionStart hasn't been disabled by default
165+
*
166+
* @returns {boolean}
167+
*/
168+
function isStateChangePermissionStartDefaultPrevented() {
169+
return $rootScope.$broadcast('$stateChangePermissionStart', toState, toParams).defaultPrevented;
170+
}
61171
});
62172
}]);
63-
}());
173+
}());

src/permission.svc.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,15 @@
127127

128128
$q.when(validationResult)
129129
.then(function () {
130-
dfd.resolve();
130+
dfd.resolve(currentPermission);
131131
})
132132
.catch(function () {
133133
findMatchingRole(permissions, toParams)
134134
.then(function () {
135-
dfd.resolve();
135+
dfd.resolve(currentPermission);
136136
})
137137
.catch(function () {
138-
dfd.reject();
138+
dfd.reject(currentPermission);
139139
});
140140
});
141141
} else {
@@ -158,11 +158,11 @@
158158
var deferred = $q.defer();
159159

160160
findMatchingRole(permissions, toParams)
161-
.then(function () {
162-
deferred.resolve();
161+
.then(function (permission) {
162+
deferred.resolve(permission);
163163
})
164-
.catch(function () {
165-
deferred.reject();
164+
.catch(function (permission) {
165+
deferred.reject(permission);
166166
});
167167

168168
return deferred.promise;
@@ -180,11 +180,11 @@
180180
var deferred = $q.defer();
181181

182182
findMatchingRole(permissions, toParams)
183-
.then(function () {
184-
deferred.reject();
183+
.then(function (permission) {
184+
deferred.reject(permission);
185185
})
186-
.catch(function () {
187-
deferred.resolve();
186+
.catch(function (permission) {
187+
deferred.resolve(permission);
188188
});
189189

190190
return deferred.promise;

0 commit comments

Comments
 (0)