Billy Logan
Billy Logan

Reputation: 2520

How to implement authorization?

Suppose, I have a model called Animal. This model contains enum attribute with two possible states.

class Animal < ActiveRecord::Base
  enum kind: [ :cat, :dog ]
end

Then in my controller I create different instance variables.

class AnimalsController < ApplicationController
  def index
    @cats = Animal.cat
    @dogs = Animal.dog
  end
end

In my view I got two separate collections.

<h1>Animals</h1>

<%= render partial: 'animals/cat', collection: @cats, as: :cat %>
<%= render partial: 'animals/dog', collection: @dogs, as: :dog %>

How can I make an authorization to be able to edit the first collection's resources and not to be able to edit the second ones?

The following approach won't work because it works only for one action entirely.

before_action :current_user_only, except: [:edit]

So, how do I implement that kind of authorization?

Thanks in advance!

Upvotes: 0

Views: 70

Answers (1)

Richard Peck
Richard Peck

Reputation: 76774

Authorization - in any capacity - is typically denoted by two patterns:

  • record/object based
  • role/user based

What you seem to need is record/object based authorization; whereby a user can edit an object if it matches certain criteria.

The most efficient way to do this in Rails is with a gem called Pundit, although I prefer CanCanCan (originally CanCan):

#Gemfile 
gem "pundit"

#app/policies/animal.rb
class AnimalPolicy
  attr_reader :user, :animal

  def initialize(user, animal)
    @user   = user
    @animal = animal
  end

  def edit?
    animal.cat?
  end

  def update?
    animal.cat?
  end
end

#app/controllers/animals_controller.rb
class AnimalsController < ApplicationController
   def edit
      @animal = Animal.find params[:id]
      authorize @animal
   end

   def update
      @animal = Animal.find params[:id]
      authorize @animal
   end
end

You can then validate on the front-end:

<% if policy(animal).update? %>
   <%= link_to "Edit", animal %>
<% end %>

--

This gives you the ability to allow the user to perform whichever actions you deem appropriate.


Update

Since you wish to evaluate users as well as objects, you're quite fortunate that both Pundit and CanCanCan support users by default:

#app/policies/animal.rb
class AnimalPolicy
  attr_reader :user, :animal

  def initialize(user, animal)
    @user   = user
    @animal = animal
  end

  def edit?
    user.moderator? && animal.cat?
  end

  def update?
    user.moderator? && animal.cat?
  end
end

The ultimate point to remember is that authorization is a boolean pattern - unless true deny access. This means that you just have to provide the conditional logic in your authorization systems (as above), to return either true or false.

Upvotes: 1

Related Questions