Nikolay Rys
Nikolay Rys

Reputation: 147

Cancan ability definition: whole controller as an object

My question is absolutely theoretic, like "Is it right thing to do?". I'm new to Rails in particular and to Ruby in general, and I'm trying to use Cancan autorization solution for my Rails appilcation.

Let's consider we have a simple contoller like this, a pair of associated views and an User model with DB table.

class UsersController < ApplicationController
  def index
    @users = User.all
  end
  def show
    @user = User.find(params[:id])
  end
end

The goal is to restrict access to the "index" method to all but admins and permit regular users to see only their own pages, e.g. to permit user with id==5 to see page "users/5". For this scope I've create an ability class for Cancan. Here it is:

class Ability
  include CanCan::Ability

  def initialize user, options = {}
    default_rules
    if user
      admin_rules(user) if user.role.eql? "admin"
      player_rules(user) if user.role.eql? "player"
    end
  end

  def admin_rules user
    can :read, UsersController
  end

  def player_rules user
    can :read, User do |user_requested|
      user_requested.id == user.id
    end 
  end

  def default_rules 
  end
end

My question is that: Should I use UsersController as an object in "can" method if I do not have a handy one of type User? To applicate it later by "authorize! :show, UsersController" in the "index" method of the controller. Or it should be done in some other way? Thank you for your suggestions.

Upvotes: 0

Views: 687

Answers (4)

Rafael Perea
Rafael Perea

Reputation: 89

+1 to @Tigraine.

Follow his instructions...

class Ability
  include CanCan::Ability

  def initialize user, options = {}
    default_rules
    if user
      admin_rules(user) if user.role.eql? "admin"
      player_rules(user) if user.role.eql? "player"
    end
  end

  def admin_rules user
    can :manage, User
  end

  def player_rules user
    can :manage, User :id => user.id
  end

  def default_rules 
  end
end

and do this in your controller...

class UsersController < ApplicationController
  load_and_authorize_resource
  # => @users for index
  # => @user for show

  def index
  end
  def show
  end
end

for details on load_and_authorize_resource see the bottom of this link

Upvotes: 0

Rafael Perea
Rafael Perea

Reputation: 89

In the wiki I found another way to set the ability. It's kind of advanced though, check it out here.

   ApplicationController.subclasses.each do |controller|
        if controller.respond_to?(:permission)  
          clazz, description = controller.permission
          write_permission(clazz, "manage", description, "All operations")
          controller.action_methods.each do |action|
...

Upvotes: 0

Tigraine
Tigraine

Reputation: 23648

No you don't want to add the UsersController to CanCan.

CanCan is meant to authorize resources, not Rails Controllers.

I would suggest the following:

def initialize(user)
  if user.is_admin?
    can :manage, User
  else
    can :manage, User, :id => user.id
  end
end

This would allow the user only access to his own user unless he is an admin. See the Defining abilities page in CanCan Wiki

Upvotes: 2

Edward Simpson
Edward Simpson

Reputation: 38

I use a symbol, e.g., in the Ability class

def initialize(user)
  if user.is_admin?
    can :any, :admin
  end
end

and in the controller

authorize! :any, :admin

Upvotes: 0

Related Questions