0

In a single page application we're building with angularjs and a Rest API in python, we have an ACL logic in the Rest API that determines if the current logged in user has access to a certain resource or not. What we are currently doing is to wrap the markup of each "protected" template in a div that contains an ng-if="authorized" attribute where the $scope.authorized variable gets true if the API responds that the current user is authorized to view the content of the template.

With this solution, what the end user experiences, is a quick blank template between the time the API takes to determine if they are authorized to access the resource and the time it takes to redirect them to a dashboard page (a page that all kinds of users have access to) along with a message that tells them that they are not authorized to access that particular resource. Of course, our concern is the time that the blank template is displayed to the end user before the application takes them to the dashboard page.

Is there something we can do in AngularJS to avoid that span time when the blank template is displayed to the end user and instead make them keep in the current page and only go to the page they requested only if the API resolved that they have access to it?

5
  • did you try pre loading the permissions and validating against those cached values Commented Aug 23, 2014 at 5:32
  • @buddhi, interesting. We haven't pre-loaded any permissions in the client side for security reasons. Which would be a safe way to do it? Commented Aug 23, 2014 at 5:40
  • yes there is a security issue, may be you could load the templates, show them disabled while you fetch the security permissions Commented Aug 23, 2014 at 5:50
  • 1
    Maybe the $routeProvider resolve config property can help you. You can stop resolution of the route till the authorization check is done. Commented Aug 23, 2014 at 5:53
  • Oh, I think that resolve is what we actually need. From AngularJS documentation it looked very confusing. Thanks a lot for your suggestions. Commented Aug 23, 2014 at 6:03

1 Answer 1

1

Do you have to use ngRoute? ui-router has two excellent tools that you could use either separately or together to solve this:

  1. It has a $stateChangeStart event that gets fired before a state transition occurs, with all information about the previous/next states. You can stop state transitions here. For example, I use something like this to send auth-required accesses to a login experience:

    $stateProvider.state('auth-required-state', {
        url: '^/auth-required-state',
        templateUrl: '/views/view/auth-required-state.html',
        controller: 'AuthRequiredStateController',
    
        // NOTE: You have access to this entire block in $stateChangeStart, so you can
        // define your own attributes that your own code looks at
        allowAnonymous: false
    });
    

    and in $stateChangeStart:

    // Handle some checking that needs to be performed
    $rootScope.$on('$stateChangeStart', function(e, toState, toParams, fromState, fromParams) {
        // If we aren't logged in and we need to be, do that
        if (!$rootScope.currentUser.loggedIn && !toState.allowAnonymous) {
            $state.go('login');
            e.preventDefault();
        }
    });
    
  2. It has a resolve operator that you can use to lazy-load things like if you need a data block from a REST service, not just a template. For another use-case I had, I only wanted to go to the target screen if the user had access to that data - the template was always available because you could access some records but not others. You can do something like this:

    var ShowDataControllerConfig = function($stateProvider) {
        var getDataBlock = function($stateParams, myDataManager) {
            return myDataManager.getDataBlockAndReturnAPromise($stateParams.data_id);
        };
    
        // Tolerate minification - you can't use the array trick in state definitions
        getGroupEntry.$inject = ['$stateParams', 'myDataManager'];
    
        $stateProvider.state('show-data', {
            url: '^/show-data/{data_id}',
            templateUrl: '/views/view/show-data.html',
            controller: 'ShowDataRoomController',
            resolve: {
                dataBlock: getDataBlock
            }
        });
    };
    
    ShowDataControllerConfig.$inject = ['$stateProvider'];
    
    var ShowDataController = function(dataBlock) {
        // dataBlock contains our data - but unlike ngRoute, we never even get here if
        // it couldn't be loaded. You can do error handling in $stateChangeError.
    };
    
    ShowDataController.$inject = ['dataBlock'];
    
    angular
        .module('myApp.controllers.showData', [])
        .config(ShowDataControllerConfig)
        .controller('ShowDataController', ShowDataController);
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.