Reputation: 2520
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
Reputation: 76774
Authorization - in any capacity - is typically denoted by two patterns:
record/object
based role/user
basedWhat 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