I want 3 user levels as Admin ,Manager,Customer in my rails application. So i've created a devise model as Users and added a migration to add the user role to it.So when a user is signed up it stores the users role(whether he is an admin,a manager or a customer). And in my application there are models and controllers for product,delivery,services. And I want to set access levels to each models.
So Admin have access to all models, controllers
Manager have access to Product, Delivery
Customer have access to Services
And i've written the Ability model as follows
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.roles == "admin"
can :manage , :all
elsif user.roles == "manager"
can :read, Products, Delivery
elsif user.roles == "customer"
can :read, Services
end
end
end
And in my product's show view i've the following code
<% if can? :manage ,@products%>
<h1>Products</h1>
<% @products.each do |product| %>
<p> <%= product.name%>
<p> <%= product.price %><br>
<p> <%= product.qty %><br>
<%end%>
<%end%>
But even i sign in as an admin i can't see the content .But when i remove the ability checking lines it can be viewed without having any issue.So i predict that the issue should be in the way that have used authorization. And My user model is as follows.
class User < ApplicationRecord
belongs_to :role
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
ROLES = %i[admin manager customer]
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :role)
end
end
And in views/devise/registrations/new.html.erb I've included the following code block.
<div>
<%= f.collection_select(:role, User::ROLES, :to_s, lambda{|i| i.to_s.humanize}) %>
</div>
This is what i have got to check the ability in my products/show view
<% if can? :manage ,@products%>
<h1>Products</h1>
<% @products.each do |product| %>
<p> <%= product.name%>
<p> <%= product.price %><br>
<p> <%= product.qty %><br>
<%end%>
<%end%>
user.roles
return an array and you are comparing array with string user.roles == "admin"
which will always be false
.
Instead use methods from User
model likeuser.role?(:admin)
to check the roles
So you ability should look like
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role? :admin
can :manage, :all
elsif user.role? :manager
can :read, [Product, Delivery]
elsif user.role? :customer
can :read, Service
end
end
end
Now in your controller you should have written something lie
class ProductsController < ActionController
load_and_authorize_resource
def index
...
end
# and other resouceful actions
end
load_and_authorize_resource
does lot of magic and one of them is load all the products on which current_user
(devise user) has access to.
Now in view, you don't have to explicitly check <% if can? :manage ,@products%>
. @products
will have only products which the current user has access to.
<h1>Products</h1>
<%@products.present?%>
<% @products.each do |product| %>
<p><%= product.name %></p>
<p><%= product.price %></p>
<p><%= product.qty %></p>
<%end%>
<%else%>
<p>There are no products to display.</p>
<%end%>