Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails REST API permissions (CanCan) with Angular JS client. How to render UI based on permissions?

I'm building a project which works only via JSON API (Rails 4.0, PostgreSQL). It's a big application with database based permissions. And I have AngularJS app which works with this REST API.

Simplified structure:

employees >--- position ---< permission

Employee.rb

belongs_to :position

Position.rb

has_many :employees
has_many :permissions, dependent: :destroy

Permission.rb

belongs_to :position
## Columns
 # action (:manage, :read, :update, :create, etc...)
 # subject_class
 # subject

I have a problem with action buttons/links on AngularJS client side.

For example I don't want to show link "Add Order" somewhere in Angular app because Employee's position permission allows only to read resource and not to modify it:

id    action     subject_class     subject
 1    :read      Order            

How I tried to solve this problem

I create a resource GET api/v1/employees/me which returns current_employee with all his permissions:

"employee": {
    ...
    :position": {
        ...
        "permissions": {
           {"id": 1, "action": "read", "subject_class": "Order", "subject": ""},
           {"id": 6, "action": "manage", "subject_class": "Waybill", "subject": ""}
        }
     }
  }

So I have all permissions on client side, but what is the best way of beautiful integration received permissions with AngularJS applications UI?

like image 545
Pavel Tkackenko Avatar asked Jul 21 '14 06:07

Pavel Tkackenko


1 Answers

After some digging I think I have found a workable solution. It is a combination of your approach with Jon Samwell's comment above:

http://jonsamwell.com/url-route-authorization-and-security-in-angular/

and Eric Zou's blog post:

http://blog.ericzou.com/2013/05/03/using-angularjs-for-existing-rails-app/

To summarize:

  • create a rails service that responds with the cancan abilities of the current user on the rails side in json format. Looks like that is what you have started doing with your GET request for permissions. Like this(from Eric's blog) when calling /abilities.json for current user:
{
  "manage": {
    "User": true,
    "Post": false,
    ...
  },
  "read": {
    "User": true,
    "Post": true
    ...
  },
  "Update": {
    "User": true,
    "Post": false
  }
  ...
}

To get the Abilities for a specific user you would do something like Ability.new(current_user) in the controller. It returns an object with a bunch of rules. The rules have conditions which will allow you to limit the access to specific posts.

  • create a service in angular that handles parsing that works as an abstraction layer between what you get from the rails side(determines if you can access a route or can manage, read, etc a specific model( e.g., can?(:manage, post) ). You can make this service as complicated as you'd like.

  • also create a directive in angular to handle what to show. Eric suggests using ng-show to do this which is fine, but cleaning it up with something similar to Jon's suggestion: <div access="post" access-type="read" access-options="options">Secret Post</div> makes sense to me.

Make sure that you don't serve up anything from the server that you wouldn't want your users to see after they have hacked your javascript to change their permissions. This is not security, it is UI management.

like image 176
user1881102 Avatar answered Sep 23 '22 08:09

user1881102