#386 Authorization from Scratch Part 2 pro
Oct 11, 2012 | 20 minutes | Security, Authorization
This finishes the series on building authorization from scratch by refactoring the permission logic into a DSL, restricting authorization with attributes, and combining with strong_parameters to protect params.



In regards to the use of
Array(controllers)to enumerate over potentially singular things, I have been using another technique I saw at http://www.rubyinside.com/21-ruby-tricks-902.html #18:[*controllers].each...Is there anything wrong with that form that I don't know about?+1
+1
Very nice! Especially liked that it was all done with testing.
Not sure that I like putting permissions definitions into separate files though. I would expect to see those directly in controllers and models defined with class method calls. The same way as attr_accessible is used - right from the model - for example. But maybe I just don't see all the benefits of using separate files yet, need to play with the code first I guess. Or maybe it's just a matter of taste :)
Anyway, great learning material!
Is there a way to whitelist for any actions within a controller using this method?
In the method you suggested in part 1, we did this:
A new-bee here. Can someone kindly explain what the 'resource' argument refers to. I am reading the code and I know what 'controller' and 'action' are referring to, but I am clueless about the 'resource' being passed as an argument stands for.
Many thanks.
A link to the code on railscast repo: http://bit.ly/TceGen
app/models/permissions/base_permission.rb
'resource' is the model that gets checked. Ryan's example is that you can only edit a topic if you created it. So, resource would be the Topic model.
Watch around the 5:00 mark.
Many thanks @Scott for clarifying that.
This is most mind-expanding episode I've watched so far. I can't even get myself to code today because I keep thinking of ways to implement this into my project. It's possible I could remove dozens of files and lots of complexity, while improving the testability with this method.
There's nothing like having the author of cancan explain authorization. Thank!
any ideas how to implement the accessible_by for the index action?
like with cancan:
@projects = Project.accessible_by(current_ability)
# This module adds the accessible_by class method to a model. It is included in the model adapters.
# An alternative action can optionally be passed as a second argument.
#
# @articles = Article.accessible_by(current_ability, :update)
#
# Here only the articles which the user can update are returned.
Permissions.permission_for(user) shows nearly what i'm looking for, but it doesn't work for the index action where a the customer_id matches.
still didn't found a way to do it, but the multi tenancy casts are a great help to succeed with my goals.
thanks so much to ryan for all the great episodes!
Can anyone point me in the right direction? I want to allow a user to only show, edit, update, or destroy their own user resource? I've implemented the @current_user caching from rails cast #274 Remember Me & Reset Password
I get caught in an infinite redirect loop when a normal user tries to show their own user profile. Any tips or suggestions would be appreciated. Mahalo
Figured out the answer to my problem....I forgot to override @current_resource
Hey thanks so much for sharing your code Spencer, it really helped me with customizing this episode to my project. You're awesome!
I was able to accomplish the same thing by having an @user = current_user for show, edit, update and destroy in the Users controller. However, your solution or mine, seems to remove the ideal of having all authorization located in one place given we have to adjust the Users controller to make things work there.
I'd guess the solution you came to with the do block in your member permission is the right direction. I'm going to play with that some.
How do you adapt when authorization depends on a parent model when the resource is not a singleton? Say
topics#indexcan only be accessed if the forum is public. Thetopics#indexreturns an array, not a singleton, so we can't calltopics.forum.private?What would be really helpful is how to extend this to handle nested resources... Has anyone extended this to use nested resources and have some guidance to share?
Not sure if this is the most elegant, but this works if you want permission.rb working with Carrierwave.
References:
strong parameter example for hashes with integer keys
strong_parameters#nested-parameters
Fails if there is more than one nested resource. In my case I have: Timesheet-> accepts_nested_attributes_for :activities, Activity -> accepts_nested_attributes_for :time_entries. In permission file I declared it as follows:
allow_param :timesheet, [:status, :user_id, :start_date, :end_date] allow_nested_param :timesheet, :activities_attributes, [:task_id] allow_nested_param :activity, :time_entries_attributes, [:workdate, :worktime]Nevertheless, I always get the error that time_entries attributes is empty. Any idea ? Thanks and regards
How would you use this authorisation logic with Devise?
I tried:
module Permissions class GuestPermission < BasePermission def initialize allow :devise_sessions, [:new, :create, :destroy] allow :sessions, [:new, :create, :destroy] allow :user_sessions, [:new, :create, :destroy] end end endBut a guest is never able to login :(
Something like this worked for me:
def initialize(user) allow :home, :index ... allow "devise/sessions", [:new, :create] allow "devise/registrations", [:new, :create, :destroy] allow_param :user, [:name, :email, :password, :password_confirmation, :remember_me] if user allow "devise/registrations", [:edit, :update, :cancel] allow "devise/sessions", [:destroy] ... allow_all if user.admin? endHope this helps
I too would be very interested in seeing how to tackle authorization on an index page where the user should only see a list of appropriate objects.
Do you have plans to make another video or perhaps just show some example code for how this might be done?
I don't understand this line in the allow? method.
Could someone break it down for me please?
+1
from the line above the line you show,
allowis eithertrueor is ablockor isnil. The line you show then checks that (1)allowedis notniland either (2a)allowedistrueor (2b)allowedis a block which returnstruewhen passedresource(andresourcemust not benil)class Permission
def initialize(current_user)
allow :index_page, [:index]
allow :static_pages, [:about, :contact]
allow :sessions, [:new, :create, :destroy]
allow :users, [:edit, :update]
end
end
but there is something wrong
NoMethodError in PrimaryPagesController#index
undefined method `allow' for #Permission:0x007fcaee54a310
Rails.root: /Users/freshlhy/rails_projects/first_app
Application Trace | Framework Trace | Full Trace
app/models/permission.rb:3:in
initialize'new'app/controllers/application_controller.rb:12:in
app/controllers/application_controller.rb:12:in
current_permission'authorize'app/controllers/application_controller.rb:20:in
How is it possible to use that with nested attributes (forms). I have my models defined as follows:
class Timesheet < ActiveRecord::Base belongs_to :user has_many :activities, dependent: :destroy, inverse_of: :timesheet has_many :time_entries, through: :activities accepts_nested_attributes_for :activities, allow_destroy: true end class Activity < ActiveRecord::Base belongs_to :timesheet, inverse_of: :activities belongs_to :task has_many :time_entries, order: :workdate, dependent: :destroy, inverse_of: :activity accepts_nested_attributes_for :time_entries, allow_destroy: true, reject_if: proc { |a| a[:worktime].blank? } end class TimeEntry < ActiveRecord::Base belongs_to :activity, :inverse_of => :time_entries validates :worktime, presence: true, inclusion: { in: [0.5, 1] } validates_presence_of :activity endActually it does not work. As far I could see at Ryan's strong_parameters rails casts github https://github.com/railscasts/371-strong-parameters, he managed to do that (not very dynamically as he said and only for one post). In my case I have 1 or more activity with 7 time_entries each. Any idea how to do that? Thank you.
First sign in through GitHub to post a comment.