1

Is there an authorization gem in Rails that handles something such as the following:

I have a user that wants to update their own profile. They'll invoke an HTTP POST to something such as http://example/users/:user_id/profile.xml. The problem is, a lot of the code to handle that has something such as:

if params[:user_id] == current_user.id
  # allow update!
else
  # don't allow update

What authorization gem will allow for abstracting this out so that specifically, that logic can be automagically implied without me having to check it manually in every method that requires it?

randombits
  • 47,058
  • 76
  • 251
  • 433

3 Answers3

0

Authority

Your basic case would be handled by a simply finding the resource this way:

def update
  # Best practice: only look for resources that belong to this user
  @post = User.posts.find(params[:id])
  ...
end

If you need additional checks, my Authority gem makes it pretty easy. You define who can do what to resources by writing methods like updatable_by? on Authorizer classes and letting your models delegate those questions to the authorizer. That way, models that have the same rules can just use the same authorizer.

In your controllers, you get some nice convenience methods:

class FoosController < ApplicationController

  # Sets up some before_filter checks at the class level. In other words, 
  # if the user can never update any instance of Foo, they'll never even reach
  # the update method
  authorize_actions_for Foo

  ...

 # Before this ever runs, we'll have asked if any Foo is ever `updatable_by?`
 # the current user; if not, they'll see your "access denied" page.
 def update
   # If they got this far, they can at least update **some** Foo instances
   # (though maybe not this one)
   @foo = current_user.foos.find(params[:id]) # this alone handles ownership check

   # Now that we know the specific instance, we can ask whether **this** Foo is
   # `updatable_by?` the user. This shorthand method checks with the authorizer and
   # shows the "access denied" page if the answer is false.
   authorize_action_for(@foo)
   ...
 end  

end

Since you're just writing plain Ruby methods on the authorizers, you can check anything you know how to check in Ruby.

def updatable_by?(user)
  resource.community_owned? && user.points > 20 || user.has_role?(:moderator)
end

See the detailed README for more info.

Nathan Long
  • 122,748
  • 97
  • 336
  • 451
0

First of all you don't want a gem, you are going to have to write your own code.

that logic can be automagically implied without me having to check it manually in every method that requires it?

That's what before_filters do. You will most likely already have one set up in your application_controller.rb. A before filter will ensure that the authorize method is called before every action that needs it See here unde section 7 Filters for more details on this http://guides.rubyonrails.org/action_controller_overview.html

Lastly, I hope you are not implying that xml post requests to update a profile should not log in! They surely should, http basic authentication will handle this for you.

See here http://api.rubyonrails.org/classes/ActiveResource/Base.html

Also for Rails => v 3.1 http://railscasts.com/episodes/270-authentication-in-rails-3-1

Update

It is clear from the comments below that it is not obvious how the suggestions I have provided help solve you problem

So here is how you could use the above information. A before filter in the application controller will ensure that the current user is ok so you don't need to check your controller params for a user id being == to the current users user id.

Secondly, if you want to know if a user is allowed to do a specific thing then add a method to the user class. something like

def can_do_something?
  #Put your code here to check if something is allowed for this specific user
end

Then instead of

if params[:user_id] == current_user.id
  # allow update!
else
  # don't allow update

You add a validation to the model that you are trying to update (which is probably related to the user in some way) then you need no additional controller code at all above the authorization and authentication checks to deal with this or if that's not a good fit and you really have to put code in your controller then do this

if current_user.can_do_something?
  # allow update!
else
  # don't allow update
jamesc
  • 12,423
  • 15
  • 74
  • 113
  • The question is about authorization, not authentication. These are different things. – RocketR Jul 28 '11 at 20:51
  • The answer to the question involves both authorization and authentication. Sorry you are wrong! authentication is a part of authorization. you won't authorize an unauthenticated user – jamesc Jul 28 '11 at 21:04
  • Considering that he already has `current_user` method, I assume the users are already being authenticated. – RocketR Jul 28 '11 at 21:06
  • It is quite clear that the quesrtion is related to checking a current_user being equal to the params for a user id. params are passed in to controller actions and used in an authorze filter to decide whether or not a user can do something. It may even involve asking the user class if the user is able to do this by implementing a can_do_something? method. Perhaps I should add that to my answer – jamesc Jul 28 '11 at 21:28
0

Your question involves 2 parts:

  1. Authentication
  2. Authorization

Authentication in this case is basically the concept of allowing users to sign in and sign up to your website. You haven't mentioned this in your question, so maybe you've already implemented it. If you haven't, I'd advise you to look into Devise (here), its really easy to use but also really powerful.

Authorization is specifically what you asked about. That is, once a user is signed in, what privileges does that give him vs someone who's not. Also, what extra privileges do admins have and so on. Like others before me suggested, the best way to do this is to use CanCan (here).

I'm using both of these in the first rails app I've ever made, they are really easy to use.

Also, if you don't want such an extensive solution to authorization like CanCan, you could simply use a before_filter and place your repeated code in there. I used this approach in another application, this post will help you out: (here)

Community
  • 1
  • 1
ankit
  • 3,328
  • 3
  • 26
  • 39