Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the proper way to hide/show AngularJS, or any other single page application ui components based on authenticated user rights?

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 ?

like image 721
Capybara Avatar asked Jun 26 '14 18:06

Capybara


People also ask

How do you secure endpoints in spring boot?

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.

What happens when we just add the spring boot starter Security dependency to your REST API?

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.


3 Answers

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>
like image 133
cuttlas Avatar answered Oct 19 '22 23:10

cuttlas


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>
like image 38
Jon Avatar answered Oct 20 '22 00:10

Jon


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.

like image 36
Sid Avatar answered Oct 19 '22 23:10

Sid