I have an application that uses Spring Security for server side authentication/authorization, Spring MVC for the REST server side endpoints, and AngularJS for view.
In the server side I've implemented all the filters needed for accesing all those REST endpoints, based on the user rights. My question is, how should I approach making visible/hiding html elements, based on the authenticated USER rights?
For example I have in the view 3 buttons (button1, button2, button3). Each button has a corresponding USER RIGHT, that should make them visible/hidden. Let's call that rights USER_RIGHT1, USER_RIGHT2, USER_RIGHT3.
If the user has the right USER_RIGHT1 he should see in the view the button1, if he has the right USER_RIGHT2 he should see in the view the button2, and so on.
My approach was to have a list of the authenticated user rights in the client, and do something as the following example:
<div ng-if="rights contains USER_RIGHT1">
<button name="button1".... />
</div>
<div ng-if="rights contains USER_RIGHT2">
<button name="button2".... />
</div>
I am not sure if the authenticated user right list should be in the client.
How should I approach this problem ? Am I doing it correctly ?
You do that by configuring Spring Security in the application. If Spring Security is on the classpath, Spring Boot automatically secures all HTTP endpoints with “basic” authentication. However, you can further customize the security settings. The first thing you need to do is add Spring Security to the classpath.
If a Spring Boot Security dependency is added on the classpath, Spring Boot application automatically requires the Basic Authentication for all HTTP Endpoints. The Endpoint “/” and “/home” does not require any authentication. All other Endpoints require authentication.
My approach is basically what you suggest.
You could have a factory which stores the array of permissions of the user and has a function to check if a permission is in the array:
.factory('Auth', function() {
var permissions = [];
return {
allowed : function(permission) {
return _.contains(permissions, permission);
}
};});
Then you can have a directive, that shows/hides an element using the service:
.directive('allowed', function(Auth){
return {
link : function(scope, elem, attr) {
if(!Auth.allowed(attr.allowed)){
elem.hide();
}
}
};
});
So in your views you only have to do:
<div allowed="permission_name"> </div>
Security on the client i.e. in a browser is next to useless. However, it should be present to stop the average user from seeing something they shouldn't however the server should be the ultimate place security is carried out.
I'd create a quick directive to do the showing / hiding or UI components and have a authentication service to do the actual logic to determine if the user has the correct rights.
I'm actual about 60% of the way through writing an in depth article about authorisation in AngularJS on my blog. I'd check back in about a week and I should have it done - it might help you with route authorisation as well.
UPDATE: The blog post about angular route authorization and security can be found here
Basically the authorisation service would authorise the user with your backend service it would then store their application rights.
The directive would then use this service to determine if the user has enough rights to see the UI component.
I haven't tested the below code so you might need to debug it.
angular.module('myApp').factory('authService', [
function () {
var loggedInUser,
login = function (email, password) {
//call server and rights are returned
//loggedInUser is assigned
},
hasSecurityRoles = function (requiredRoles) {
var hasRole = false,
roleCheckPassed = false,
loweredRoles;
if (loggedInUser === undefined) {
hasRole = false;
} else if (loggedInUser !== undefined && requiredRoles === undefined) {
hasRole = true;
} else if (loggedInUser !== undefined && requiredRoles !== undefined) {
if (requiredRoles.length === 0) {
hasRole = true;
} else if (loggedInUser.permittedActions === undefined) {
hasRole = false;
} else {
loweredRoles = [];
angular.forEach(loggedInUser.permittedActions, function (role) {
loweredRoles.push(role.name.toLowerCase());
});
// check user has at least one role in given required roles
angular.forEach(requiredRoles, function (role) {
roleCheckPassed = roleCheckPassed || _.contains(loweredRoles, role.toLowerCase());
});
hasRole = roleCheckPassed;
}
}
return hasRole;
};
return {
login: login,
hasSecurityRoles: hasSecurityRoles
};
}
]);
angular.module('myApp').directive('visibleToRoles', [
'authService',
function (authService) {
return {
link: function (scope, element, attrs) {
var makeVisible = function () {
element.removeClass('hidden');
},
makeHidden = function () {
element.addClass('hidden');
},
determineVisibility = function (resetFirst) {
if (resetFirst) {
makeVisible();
}
if (authService.hasSecurityRoles(roles)) {
makeVisible();
} else {
makeHidden();
}
},
roles = attrs.visibleToRoles.split(',');
if (roles.length > 0) {
determineVisibility(true);
}
}
};
}
]);
You would then use it like this
<div visible-to-role="admin,usermanager">.....</div>
Rather than having a hard-coded list in your templates/pages you could retrieve the authenticated user right list from the server and load it in your scope and then do the same thing that you are doing. If you are using ui-router this is possible by using the resolve property (that is pre-loading certain data possibly from the server before a controller is called).
This way you can only retrieve the rights for the user that is looking at the page rather than have all rights hard-coded in the client.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With