3

I'm building a blog system using angularJS, and now trying to build a resource authorization flow that is simple to use for certain resources. For example, I have the following states PROPOSALITEM, PROPOSALITEM.VIEW and PROPOSALITEM.EDIT. PROPOSALITEM is the parent states which resolve proposalItem by the provided :proposalId for the child states. PROPOSALITEM.VIEW is the view state which allows all users to access it, and PROPOSALITEM.EDIT is the edit page which should allow only the proposal owner to access.

I found that there's a beautiful structure to do the role based authorization flow in angularJS from this article: Techniques for authentication in AngularJS applications. However what I need now is based on the ownership of the resource to decide if the user could access the route. And I have many resources like proposal model which needs to be checked with owner authorization. All these models are provided with a owner column. Is there any general solution to deal with this? (I don't want to put the authorization code for each model in its own resolve part because it's difficult to maintain)

/* Abstract states. Resolve proposalItem */
.state('PROPOSALITEM', {
    abstract: true,
    url: '/proposals/:proposalId',
    template: '<ui-view/>',
    resolve: {
        proposalItem: function($stateParams, ProposalDataService) {
            return ProposalDataService.getItem($stateParams.proposalId);
        }
    }
})
/* View states. Allows all users to access */
.state('PROPOSALITEM.VIEW', {
    abstract: true,
    url: '/view',
    template: '...'
})
/* Edit states. Allows only the proposal owner */
.state('PROPOSALITEM.EDIT', {
    url: '/edit',
    template: '...',
    resolve: {
        proposalItem: function($q, proposalItem, UserDataService) {
            var me = UserDataService.getMe();
            // me: {
            //     _id: '1',
            //     name: '...'
            // }
            if (proposalItem.owner._id === me._id) {
                // Authorized.
                return proposalItem;
            } else {
                // Not authorized.
                var deferred = $q.defer();
                deferred.reject(ERROR_KEYS.NOT_AUTHORIZED);
                return deferred.promise;
            }
        }
    }
})
Huang Yen-Chieh
  • 635
  • 2
  • 11
  • 24
  • 1
    This is very good and important question. I actually have a working solution for this, but need to extract it from my application in a form of module before i can publish it. I plan to have that done by the end of the next week, not sure if you're willing to wait though. – appmux Jan 25 '15 at 04:14
  • Even i would be excited to know it. @appmux – micronyks Jan 25 '15 at 06:04
  • @appmux I'm not in a hurry. Very look forward to your solution! – Huang Yen-Chieh Jan 25 '15 at 08:03

1 Answers1

0

This is how I normally deal with this case. Create a service which is supposed to deal with the authentication:

yourapp.factory('YourResourcePermission', function () {
    return {
        check: function (resource) {
            var me = UserDataService.getMe();
            // return a promise which is resolved with the data
            // if the user has permission, or rejected otherwise.
        }
    };
});

Then you use this service as the resolve param for the status you need:

.state('PROPOSALITEM.EDIT', {
url: '/edit',
template: '...',
resolve: function (proposalItem, YourResourcePermission) {  
    return YourResourcePermission.check(proposalItem);
}

})

In this way you can keep all your permission logic away from the routes in a dedicated (and reusable) service.

If you have a few states requiring the same require boilerplate, you can use Angular $provide.decorator to automatically inject your custom logic to your states. See this: https://github.com/christopherthielen/ui-router-extras/blob/master/src/dsr.js#L10 and this https://stackoverflow.com/a/26848546/3794660 as references on how you can decorate the $stateProvider and create your custom state type that basically has the 'resolve' param already set.

Hope this is helpful.

Community
  • 1
  • 1
fabio.sussetto
  • 6,964
  • 3
  • 25
  • 37