Skip to content
This repository has been archived by the owner on Nov 19, 2019. It is now read-only.

How to handle index actions #24

Closed
Linuus opened this issue Feb 24, 2013 · 4 comments
Closed

How to handle index actions #24

Linuus opened this issue Feb 24, 2013 · 4 comments
Labels

Comments

@Linuus
Copy link

Linuus commented Feb 24, 2013

Hi

How do you handle index actions with Authority? In my app I have User which can be either 'user' or 'admin'. They belong to a Company.

So, a 'user' can only visit the user#show action if it's their own user record. And an 'admin' can view all users from her company:

  def readable_by?(user)
    if user.role == 'admin'
      user.company == resource.company
    elsif user.role == 'user'
      user == resource
    else
      false
    end
  end

But what about the index action? I want an 'admin' to be able to access the user#index action but not the 'user'. I tried to use this:

  def self.readable_by?(user)
    user.role == 'admin'
  end

And added: "authorize_actions_for User" in the controller
But that doesn't work since it restricts a 'user' to read any user.

Am I supposed to just use authorize_action_for and pass the class instead of an instance? It seems to work.

  def index
    authorize_action_for User
    @users = current_user.company.users.order("name ASC")
  end

But then I'm a bit confused about when to use the "authorize_actions_for User" filter?

@nathanl
Copy link
Owner

nathanl commented Feb 25, 2013

@Linuus - The issue here is your controller action map. By default, both index and show check readable_by?. But for your user controller, you're considering those as actions that require different permissions.

You need one of these to be a different verb and adjective. For instance, maybe you'd consider index to be the odd one; for it, instead of checking if something is readable, you'd check if it's "listable". First, you'd add ":list => :listable" to config.abilities, so that Authority knows to call user.can_list? and pass that to resource.listable_by?. Then, in your User controller, you'd tell it that when someone goes to the index action, we should check whether they can list users:

class UsersController < ApplicationController
   authorize_actions_for User, :actions => {:index => :list}
end

Give that a try and let me know how it goes.

@nathanl
Copy link
Owner

nathanl commented Mar 1, 2013

I'm going to close this issue for now, but feel free to reply if you want to discuss this more.

@nathanl nathanl closed this as completed Mar 1, 2013
@Linuus
Copy link
Author

Linuus commented Mar 13, 2013

Ok, I've done that now and it seems OK :)

However, I don't really see any use case for the class methods other than for index actions when there is no single resource.

For instance, if you're never allowed to delete a user, is it any difference between using class or instance methods?

  def deletable_by?(user)
    false
  end

vs

  def self.deletable_by?(user)
    false
  end

Or, should I still create both? Any best practices? :)

@nathanl
Copy link
Owner

nathanl commented Mar 14, 2013

@Linuus - The controller is going to check the class method in its before_filter, so it's used for every controller action. That's because even if the action is destroy, you won't have a user instance yet when the before_filter runs; you won't have an instance until you get inside your controller action and look one up by id. So it needs to ask "before I figure out which user you're trying to delete, are you ever allowed to delete any user? If not, let's just stop right here."

If nobody is ever allowed to delete a user, the class method should simply be:

def self.deletable_by?(user)
  false
end

In this case, there's no need to define an instance method, because the authorizer inherits an instance method that just calls the class method. The logic being, "if you're not allowed to delete ANY user, clearly you're not allowed to delete THIS user."

If the class method sometimes returns true, you only need an instance method if there are instances to which that class-level authorization doesn't apply. For example:

class UserAuthorizer < Authority::Authorizer

def self.deletable_by?(user)
  # If you're not an admin user, you can never delete any user, period.
  # If you are an admin user, you can at least delete some users.
  user.admin?
end

# If you don't define this method, it will fall back to the class method, so an
# admin will be able to delete any user instance
# If you do define this method, you can use it to say "except user instances like X"
def deletable_by?(admin)
  resource.title != "CEO" # If this user is the CEO, even an admin can't delete him/her
end

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants