#188
Nov 16, 2009

Declarative Authorization

Declarative authorization provides an advanced and powerful solution for role based authorization.
Download (36.4 MB, 15:27)
alternative download for iPod & Apple TV (21.6 MB, 15:27)

Resources

sudo rake gems:install
# config/environment.rb
config.gem "declarative_authorization", :source => "http://gemcutter.org"

# config/authorization_rules.rb
authorization do
  role :admin do
    has_permission_on [:articles, :comments], :to => [:index, :show, :new, :create, :edit, :update, :destroy]
  end
  
  role :guest do
    has_permission_on :articles, :to => [:index, :show]
    has_permission_on :comments, :to => [:new, :create]
    has_permission_on :comments, :to => [:edit, :update] do
      if_attribute :user => is { user }
    end
  end
  
  role :moderator do
    includes :guest
    has_permission_on :comments, :to => [:edit, :update]
  end
  
  role :author do
    includes :guest
    has_permission_on :articles, :to => [:new, :create]
    has_permission_on :articles, :to => [:edit, :update] do
      if_attribute :user => is { user }
    end
  end
end

# application_controller.rb
before_filter { |c| Authorization.current_user = c.current_user }

protected

def permission_denied
  flash[:error] = "Sorry, you are not allowed to access that page."
  redirect_to root_url
end

# articles_controller.rb
filter_resource_access
<!-- articles/show.html.erb -->
<p>
  <% if permitted_to? :edit, @article %>
    <%= link_to "Edit", edit_article_path(@article) %> |
  <% end %>
  <% if permitted_to? :destroy, @article %>
    <%= link_to "Destroy", @article, :method => :delete, :confirm => "Are you sure?" %> |
  <% end %>
  <%= link_to "Back to Articles", articles_path %>
</p>
...
<p>
  <% if permitted_to? :edit, comment %>
    <%= link_to "Edit", edit_comment_path(comment) %>
  <% end %>
  <% if permitted_to? :destroy, comment %>
    | <%= link_to "Destroy", comment, :method => :delete, :confirm => "Are you sure?" %>
  <% end %>
</p>

<!-- articles/index.html.erb -->
<% if permitted_to? :create, Article.new %>
  <p><%= link_to "New Article", new_article_path %></p>
<% end %>

RSS Feed for Episode Comments 89 comments

1. maurizio de magnis Nov 16, 2009 at 04:47

was waiting for this ;)


2. Ryan Nov 16, 2009 at 05:17

We use easy_roles at platform45, for simple role based authorization.

Its more of a light weight solution, and has basic usage, not comparable to declarative authorization.

I think it's worth checking out tho!

http://github.com/platform45/easy_roles


3. jeremy.f Nov 16, 2009 at 05:22

Wonderful stuff.
Wonderful gem.
Wonderful railscast.
Wonferful you !

Thanks again Ryan !


4. kikito Nov 16, 2009 at 05:39

Hi Ryan,

There's no need to create an article on this line:

<% if permitted_to? :create, Article.new %>

instead, you can do just this:

<% if permitted_to? :create, :articles %>

Appart from that, great stuff!


5. jDeppen Nov 16, 2009 at 05:40

Awesome Ryan, thanks again for your amazing screencasts. I'm going to implement this in my app today.

I'm making a PayPal donation, please keep the episodes coming.


6. ARTSIOM Nov 16, 2009 at 06:17

can someone please explaine, what this line do in ApplicationController:

include Authentication


7. Ray Nov 16, 2009 at 06:34

What is it with this site and boot spam?


8. Fredrik Nov 16, 2009 at 07:01

Sadly reCAPTCHA seems to not keep em away.


9. UserUnknown Nov 16, 2009 at 07:30

>Sadly reCAPTCHA seems to not keep em away.

Maybe we could give a try for negative captcha? I use it on many sites and it's seems to work better than classic captchas.. and without people harassment.


10. bob Nov 16, 2009 at 07:37

I think it's our responsibility to take the time to report the spam. The problem is that the ahole that is doing this knows that there is a flood of activity every monday morning so he's probably adding the spam manually.


11. Joost Saanen Nov 16, 2009 at 07:44

As Ryan mentioned earlier, this could be a human spammer. Maybe time for blocking some ip.

anyway great cast Ryan


12. Walter Nov 16, 2009 at 07:46

When you delete
@article = Article.find(params[:id]) and @article =Article.new... from the ArticlesController. You say it is now handled by the filter_resource_access before filter.

Where is this implemented? What happens if my controller differs and I need something other then .find(params[:id]) for instance when using pagination? Can we use a different before filter?


13. Adam Nov 16, 2009 at 07:52

You just made my day :)


14. andy Nov 16, 2009 at 08:02

@ARTSIOM
"include Authentication"
is for AuthLogic

@kikito
<% if permitted_to? :create, Article.new %>
instead, you can do just this:
<% if permitted_to? :create, :articles %>

that is not always the correct behavior, Article.new allows for if_attribute checks - :articles inhibits this feature


15. Patrick Nov 16, 2009 at 08:02

This is rather timely as I've been redoing some role based permissioning stuff lately. I may have to get dirty with this. Thanks Ryan.

On a side note: The ugg spam is getting a little absurd. It's obviously some jerk doing it manually. I wonder if a banned word list would help.


16. andy Nov 16, 2009 at 08:06

@Walter

filter_access_to does not auto-load these objects, filter_resource_access is a newer feature of DA

check the README


17. Anlek Nov 16, 2009 at 08:17

Hey Ryan, great work,
However I wonder how to go about doing a role based management with accounts (subdomains). So if some user has manage access in account 1 and guest access in account 2. How would you go about setting that up in declarative_authorization?

Thanks again,

Andrew


18. Caleb Land Nov 16, 2009 at 08:40

I like the Aegis plugin (http://github.com/makandra/aegis).

It has a lot less magic than the declarative auth plugin, but it's straightforward and clean.


19. Emanuele Barban Nov 16, 2009 at 08:41

We use this kind of approach from quite some time here at Lipsiasoft with our admin, Lipsiadmin.

Check it out.

http://www.lipsiadmin.com/


20. justin Nov 16, 2009 at 08:51

looks pretty nice, fyi there's shorthand for the :to parameter so you don't have to explicitly state every approved action.

Such as :to => :manage or :to => :all


21. Michael Harrington Nov 16, 2009 at 09:20

You have a typo in the links. It says "Authologic" but should be "Authlogic".


22. mrkris Nov 16, 2009 at 09:32

Authorization plugins are a hard find. Some have barely any features, some are overly complex. One issue I'd like to point out is if someone is not authorized, it should throw perhaps a 401


23. Erik Nov 16, 2009 at 10:15

What is missing from this gem to tempt you to write your own role authorization plugin (according to your tweet)?


24. Tomash Nov 16, 2009 at 10:16

I'm a user (and guys at my company aswell) of base_auth, a pretty simple yet powerful authorization plugin. I think you could do a Railscast on that one too, now that you've touched the topic of authorization.
http://blog.aenima.pl/2008/12/8/base-auth-a-complete-tutorial-to-securing-your-rails-application

Great 'cast by the way, keep up the good work!


25. anotherdjohnson Nov 16, 2009 at 10:52

I've been using this for awhile, it's a wonderful plugin, and Steffan is always in the google forums helping out users.

I was wondering when you would do a screen cast about this wonderful gem!

Thanks for all you do, you're always saving me time, and showing me something new!


26. Alican Nov 16, 2009 at 12:35

Thank you so much, great screencast.


27. Steve Nov 16, 2009 at 12:39

Ryan - excellent, useful information this week. I've been a satisfied user of your nifty_authentication generator. For a tiny app I'm doing at work, I'd like the staff to be able to do just about anything except create a new user - I'd like only an admin to be able to do that. I've been trying to figure out how to do roles with nifty_authentication since for this project even something as straightforward as authlogic is more complexity than I need. So here's my encouragement for an rbates authorization solution that's just a LITTLE more full featured than nifty_authentication!


28. Seth Crosby Nov 16, 2009 at 12:48

Thanks for all your work, Ryan. These are great, and your time and effort are much appreciated.

I'm sorry you've gotten hammered by the comment spammers. Really sucks that you have to deal with that. Wish I could help!


29. Jochen Kempf Nov 16, 2009 at 14:01

I really like this declarative approach!

For those who need to control non-action related content and keep the declarative approach - this is what works perfectly for me:

- Create a dummy action in the controller (e.g. "admin", "no_admin")
- define the corresponding role-based rules
- and use the Authorization view_helper (e.g "if permitted_to? :admin, :articles) in the view

The benefit is that you don't have to update your views when adding roles.


30. John Nov 16, 2009 at 14:59

It's me or the iPhone/iPod version has some artifacts and a couple times the screen gets freeze...
Except for this ;-) I think you're doing an amazing job. Thank you.


31. Garrett Bjerkhoel Nov 16, 2009 at 15:17

Well, this could solve the problem:

unless ["ugg", "nike", "boot", "boots", "cheap"].include?(comment[:comment])
   @comment.save
else
   redirect_to "http://google.com"
end

This could solve your problem.


32. Fredd Nov 16, 2009 at 15:17

Nice solution! But if I don't want to hard code the acl, what's the alternatives for storing everything in the database? I think it would be more flexible to be able to create permissions on the fly and then assign them to user groups perhaps.


33. Rolf Bjaanes Nov 16, 2009 at 15:32

I do love the typo "Authologic" in the show notes. Looking forward to watching the episode tomorrow. Keep up the good work, Ryan!


34. Clemens Kofler Nov 16, 2009 at 15:59

Sven Fuchs and I have written a really generic (and therefore powerful and not quite easy to use) RBAC implementation. You can find it here: http://github.com/svenfuchs/rbac

We use it in adva-cms (http://github.com/svenfuchs/adva_cms - http://adva-cms.org), so you can check the implementation there if you want to.


35. Lin He Nov 16, 2009 at 18:48

Nice episode once again! Thanks a lot!


36. dickstar Nov 16, 2009 at 19:27

how do i dynamic change the role in this program?

It looks like that I need to revise manually. I can not revise it online.


37. Joseph Silvashy Nov 16, 2009 at 20:34

Thanks Ryan, I use ACL9 gem, and I know there are probably like 100's of ways to go about authorization. Excellent screen cast as usual.

Has it struck anyone that this is probably the worst place online to advertise for Ugg boots... seriously, we are the just a bunch of dudes that want to program, WTF why would we ever by any Ugg boots or womens purses...


38. Dave Nov 17, 2009 at 05:45

Declarative Authorization is great, but what's the deal with all the Ugg boots in the comments?

Maybe the next Railscast should be about comment moderation and reducing spam?


39. Jose Nov 17, 2009 at 07:48

The comments section at this site, really doesn't match the high quality of the rest of it. Between the boot spam and the congratulatory comments, the comments aren't providing much value.

I think the comments section should be closed to the public. Important comments, like reporting typos, or giving valuable tips, should be emailed to a moderator for consideration to post.


40. Nami-Doc Nov 17, 2009 at 09:18

Is there any code source ?


41. DGM Nov 17, 2009 at 10:29

Why not say a codeword at the end of the railscast that is needed to post a comment for it? Or some obfuscated ruby code to display a password? That might discourage the spammer...


42. Zen Nov 17, 2009 at 14:01

Is there a way to handle dynamics RBAC / allow admin users to handle RBAC instead of such hardcode ?


43. djon Nov 17, 2009 at 20:12

What here for clause? It is A lot of spam.


44. Shan Nov 18, 2009 at 01:24

Pretty good & very useful thought for SAS model applications.


45. Ricardo J. Arteta Nov 18, 2009 at 09:08

Cool cast, I build my on plugin for this, much more client oriented, but this still is cool.
(I'm submitting this comment to see if the recaptcha is really working)


46. Ricardo J. Arteta Nov 18, 2009 at 09:19

Another try with just one word of the recaptcha


47. Carl Nov 18, 2009 at 22:35

As a viewer from the beginning (or very close) I second the idea of registering to be able to comment or something like that. A passcode at the end of the cast might work, too. For whatever reason reCaptcha isn't working at all.


48. Abhishek Shukla Nov 18, 2009 at 22:43

This is what I was looking.


49. mojo2go Nov 19, 2009 at 06:51

@Jochen Kempf:

Your approach to putting access control on non-action based content in intriguing, but I cannot understand your explanation well enough to implement it. Where can I go to see this in more detail?

For example, where do you put the content to be controlled? Is it hard-coded in the view? And is the dummy controller completely empty?


50. Rimbaland Nov 19, 2009 at 07:49

Hi! I'm having this problem: i have, in my user table, a boolean column named "admin"..in my user.rb i wrote:
def role_symbol
  [:admin] if admin?
end
and in my authorization_rules i have:
role :admin do
    has_permission_on [:artists, :albums, :events, :neews], :to => [:index, :show, :new, :create, :edit, :update, :destroy]
end

the problem is that when i login as an admin user(which has admin = true) i can't reach any model which has "filter_resource_access" in its controller, i see the "You are not allowed to access this action." page...why?
Thanks a lot!


51. EL Nov 19, 2009 at 15:22

Ryan,

A couple of thoughts/temporary fixes on the spam issue.

1. If enough users click on "Report as Spam" for a message, hide the detail of the message. Put a "Flagged as Spam" message and an "Unhide" link - that way, you can scroll through the comments quicker.

2. Assign basic moderator functionality and assign a handful of trusted readers to remove the spam.

3. Implement the keyword blacklist that others have suggested. If a few valid comments don't make it through, it isn't that big a deal. It's not like we're sending an innocent man to jail.


52. Nami Nov 20, 2009 at 00:59

I have a problem: when I register, I have no "role" :|


53. raja Nov 20, 2009 at 14:23

Used Declarative Authorization and Authlogic in a project together, it added some really cool advantages.


54. jDeppen Nov 21, 2009 at 12:34

Glad to see you got the spam cleaned up.

Testing new spam system:
Quality shoes ...


55. Ryan Nov 21, 2009 at 12:42

Hope the new spam system works! :)


56. Kieran P Nov 21, 2009 at 12:50

YaY! Spam is gone. I see from the Github repo that you implemented a question based spam captcha. But I'm not seeing it appear here... site needs an update?


57. Ryan Bates Nov 21, 2009 at 13:01

@Kieran, the question only shows up if the comment looks like spam. As long as you aren't grunting like a caveman (ugg, ugg) then you should be fine.


58. Michael Nov 21, 2009 at 15:08

Awesome cast again! thanks!


59. Carl Nov 21, 2009 at 19:03

@Ryan, your sense of humor gets me every time.


60. bob Nov 22, 2009 at 18:14

@ryan, I think the spammer is just playing with you at this point. You may want to consider blocking IPs.

On a related note, very excited about CanCan, will be giving it a trial run in one of my apps very soon.


61. kikiki Nov 22, 2009 at 22:00

Thanks for post.
By the way, How can be the script/console result shown like Mysql command line result?(grid like)
Is that a kind of plug-in?


62. Jay Nov 23, 2009 at 02:24

@kikiki, http://github.com/cldwalker/hirb


63. Adam Hawkins Nov 24, 2009 at 23:32

I still like my stab at authorization: http://github.com/Adman65/AccessControl


64. SKY Nov 25, 2009 at 18:18

very excited about CanCan, will be giving it a trial run in one of my apps very soon.


65. steve Nov 27, 2009 at 16:26

Hi,

I'm using authlogic + delcarative auth.

How to I get the access the name of a logged-in user's role? I need it for routing, too.

e.g.,
if session[:role] == 'admin'
  redirect_to admin_path
else
  redriect_to employee_path
end

I've tried find_by_name, but without success.

Thanks.

Steve


66. Behrang Javaherian Nov 28, 2009 at 05:21

Thanks for the post. For the views we can replace the
<%if ..%>
<link_to ....
<%end%>
with link_to_if method. It is much nicer.

Cheers
Behrang


67. Daniel Lopes Nov 30, 2009 at 06:16

Why you didn't use your gem can can?


68. evanmcd Dec 01, 2009 at 08:23

When I took out the model reference stuff (@articles = Article.find(:all) ) I ended up with no data. Any reason why filter_resource_access wouldn't be doing what it's supposed to?

Thanks for the very helpful show.

Evan


69. jadefalkner Dec 03, 2009 at 01:49

I have a Problem with observed_field

if i want to update, it is only
possible if the Controller will not
be filtered, otherwise it says that
the method isn't found. Well, i added
the method tho the authorization-file.

Strange thing... can anyone help?


70. Leo Wong Dec 05, 2009 at 05:25

I found a dark corner in declarative_authorization usage. If you defined a privilege in authorization_rules.rb:

  privilege :update, :includes => :edit

you can't just use:

  filter_access_to :update

in the controller definition when you really mean:

  filter_access_to [:edit, :update]


71. psingh Dec 05, 2009 at 06:33

even if we hide links using below code, any one can directly go to the url and access the page:

<% if permitted_to? :edit, @article %>
    <%= link_to "Edit", edit_article_path(@article) %> |
  <% end %>

what if guest goes and types
/articles/new
?

don't we also need to put the check in controller?


72. Jonsey Dec 08, 2009 at 05:22

I have been experimenting with lockdown on my current project and liked it alot, configuration is all in one file, links are automatically hidden if the user doesn't have permission and you don't need to add any code to the controllers. However I found it quite difficult setting up persmissions based on the current user and also got annoyed by the requirement to restrat the server for permissions to take effect. It is a good system, but I think I will give declarative authorization a go it looks far more intuitive, it would be handy to have the lniks automatically hiden as with lockdown though, so I might have a go at implementing that in a fork.


73. Pablo Dec 09, 2009 at 08:42

hi, I'm not getting the roles in the sign-up page...

I might be something very obvious, can someone point me in the right direction?

Thanks!


74. Pablo Dec 09, 2009 at 15:06

The only way I can populate the roles is if I do this:

 <%= f.select :roles, (controller.authorization_engine.roles + (@user.roles || [])).uniq, {}, {:multiple => true} %>

but I cant save it... :(


75. emb Dec 17, 2009 at 17:16

Is there a way to customize the message or do a redirect instead of just sending "You are not allowed to access this action." if authorization fails?


76. emb Dec 17, 2009 at 18:48

Surely there are much better ways than this:

    class ApplicationController < ActionController::Base
        around_filter :authorize_redirect
        def authorize_redirect
            yield
            if response.body == "You are not allowed to access this action."
                response.redirect('/login', 550)
            end
        end
    end

but the authorization magick has already rendered, so I cannot render or redirect :(


77. emb Dec 17, 2009 at 18:57

Ok, sorry. After spending some time going through the source I found that I can just define permission_denied on my controller and it will be called and I can do whatever I want! Problem solved!


78. art Dec 18, 2009 at 19:23

hi i wanna ask that
in this program can we show user's article..what i mean when we login can we just see the article who logins..if its possible how we can do it


79. tina Dec 22, 2009 at 22:39

http://www.footwearkicks.com


80. yeni müzik Dec 29, 2009 at 15:46

This is one of those "clever" solutions that will only cause


81. martins Dec 30, 2009 at 18:37

Hi, how to authorize acces to custom methods other than CRUD? By default all non standard methods are just blocked, how to deal with it?
has_permission_on :foos, :to => :ownmethod doesnt work :/


82. women handbags Jan 19, 2010 at 08:03

nice post


83. mbt chapa shoes Jan 19, 2010 at 08:03

how to authorize acces to custom methods other than CRUD?


84. Manolo Blahnik shoes Jan 24, 2010 at 23:01

Ok, sorry. After spending some time going through the source I found that I can just define permission_denied on my controller and it will be called and I can do whatever I want! Problem solved!


85. javon Jan 25, 2010 at 22:28

called and I can do whatever I want! Problem solved!


86. javon Jan 25, 2010 at 22:39

Thank you for sharing.Nice post.


87. Senthil Jan 27, 2010 at 15:48

For those who are having trouble seeing roles in your sign up, its because you haven't populated your database with any roles.
So fire up your console (script/console) and enter

http://pastie.org/797950

You can use whatever names you want, but remember to change config/authorization_rules.rb files accordingly.


88. Maykon Jan 28, 2010 at 13:52

How would stay this example if I want to use the Authlogic? thank you ...


89. uggs online Jan 29, 2010 at 21:29

Generally I do not post on sites, but I would like to say that this blog really convinced me to do so! Thanks, very good post.

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source