views:

2043

answers:

3

There are a lot of rails plugins out there that handle user permissions. I'm impressed with the implementation in the hobo gem, but I'm not sure if I can use just this feature and not the other parts. GateKeeper is a really clever implementation, but has some bugs, though it's small enough I could probably fix it myself. Restful_ACL gives you a class method for checking creation, meaning you can't do any checks on the instance in question (not sure if it does scoped finds).

I'd like something that provides a scoped version of ActiveRecord#find which only finds things the current user is allowed to see. This should be robust enough to say, you can only see pictures that are in galleries that are owned by you or one of your friends.

As a bonus, it could prevent creates or updates (in a before_* or validation step) that you don't have the right to perform, including associating your own records with a different user or gallery, or creating such records.

A: 

You could check out lockdown. I haven't implemented this on project yet but it could save you some work.

Mike Breen
+2  A: 

You can do some really complex authorization tricks on the model level with declarative_authorization plugin. Nevertheless, I myself prefer rails-authorization-plugin - with this one you define the roles on the model level (eg. someone is an owner of a resource) and the permissions on the controller level (eg. only the owner of a resource or admin can get the resource). I find this approach much more concise, especially if you are pursuing for a clean REST approach. If you have request like this one:

GET /posts

what you really should return is all posts, not just the posts of the current user. For that purpose you should have a different route:

GET /users/:user_id/posts

where :user_id is set to current user's id. This difference should then be mirrored in the appropriate action:

def index
  user = User.find(params[:user_id]) unless params[:user_id].blank?
  @posts =
   if user
    # get all posts of a user
    user.posts.all
   else
    # get all posts 
    Post.all
   end
end

Now, what you really have here are two authorization contexts - "get all posts of a user" and "get all posts" and you usually want to set different permissions for both (eg. "only admins can get all posts" and "only user it self or an admin can get all posts of a user").

Milan Novota
I use a combination of rethful_authentication and the rails-auth plugin. Rails Auth is very powerful, and provides a complete DSL for describing your permissions.
Toby Hede
That's restful_authentication, of course :P
Toby Hede
+1  A: 

I have a gem that's entirely about being able to specify permissions for objects based on their role. You can take a look here: http://github.com/nakajima/roleful/tree/master. It would work well for the sort of before_filter approach you hinted at.

As for only allowing users to access things with which they have some sort of relationship, I'd think that you're looking at a prime candidate for Rails association proxies. I like Milan Novota's snippet, though I'd modify it slightly:

def index
  @posts = user_repo.posts
end

private

def user_repo
  # find_by_id is **much** faster than regular find,
  # plus it just returns nil when there's no record
  if user = User.find_by_id(params[:user_id])
    # returns the association proxy
    user.posts
  else
    # returns the class
    User
  end
end
nakajima