I'm currently using Devise, CanCan, and Rolify to handle authentication and authorization for my rails app and I'm having a really hard time understanding how to make it so that a user
can only :show
and :update
a specific instance of a model that the user belongs to (aka my user
has a client_id
column, not the other way around).
The update part of my defined Abilities.rb
for the user
with the :client
role, works fine, i.e. if current_user.client_id = 3
then he can only update a client where Client.id = 3
, however, that same user can SEE any instance of the Client
model and I can't seem to grasp how to limit this.
Ability.rb
...
if user.has_role? :client
can [:read, :update], [Property, Order], client_id: user.client_id
can [:read, :update], Owner
can :create, [Property, Order, Owner]
can :manage, User, id: user.id
can [:show, :update], Client, id: user.client_id
end
...
Each user does not have an index
of all Clients
, so after researching I changed can [:read, :update], Client, ..
to :show
but the users can still see the other clients
but the :update
part if it works fine, so I'm really at a loss here. Have been googling for the past few hours and read through all the CanCan documentation of which I acknowledge that it maybe addressed but I can't figure it out.
I've tried limiting it from the controller side as shown below but that doesn't work either:
external/clients_controller.rb
class External::ClientsController < ApplicationController
load_and_authorize_resource
before_filter :client_only
def index
@clients = Client.paginate(page: params[:page], per_page: 15)
end
def show
@clients = Client.find(params[:id])
@client_users = User.where(client_id: params[:id])
@client_orders = Order.where(client_id: params[:id]).includes(:property, :owners)
can? :show, @clients
end
def edit
@clients = Client.find(params[:id])
respond_to do |format|
format.html { @clients.save }
format.js
end
end
def update
@clients = Client.find(params[:id])
@clients.update_attributes(client_params)
respond_to do |format|
format.html { if @clients.save
flash[:success] = "Client Updated Successfully"
redirect_to client_path(@clients)
else
render 'edit'
end
}
format.js
end
end
private
def client_params
params.require(:client).permit(:uuid, :company, :first_name, :last_name, :phone, :email, :address1, :address2, :city, :state, :zip, :notes)
end
def client_only
redirect_to root_path unless current_user.is_client?
end
end
So if anyone could help me fully understand how CanCan handles role based authorization for an instance of a model then I would greatly appreciate it. Thanks in advance!
Updated Code
Removed all @client
instance loads in external/clients_controller.rb
class External::ClientsController < ApplicationController
load_and_authorize_resource
before_filter :client_only
def show
@client_users = User.where(client_id: params[:id])
@client_orders = Order.where(client_id: params[:id]).includes(:property, :owners).paginate(page: params[:page], per_page: 15).order("order_number DESC")
end
def edit
respond_to do |format|
format.html
format.js
end
end
def update
if params[:client][:state].blank?
params[:client][:state] = @client.try(:state)
end
@client.update_attributes(client_params)
respond_to do |format|
format.html { if @client.save
flash[:success] = "Client Updated Successfully"
redirect_to external_client_path(@client)
else
render 'edit'
end
}
format.js
end
end
private
def client_params
params.require(:client).permit(:uuid, :company, :first_name, :last_name, :phone, :email, :address1, :address2, :city, :state, :zip, :notes)
end
def client_only
redirect_to root_path unless current_user.is_client?
end
end
full ability.rb
class Ability
include CanCan::Ability
def initialize(user)
alias_action :show, :to => :view
alias_action :open_external_orders, :completed_external_orders, :to => :client_order_views
user ||= User.new
if user.has_role? :admin
can :manage, :all
can :assign_roles, User
else
can :read, :all
end
if user.has_role? :executive
can :manage, [Property, Deed, Mortgage, Order, Owner, Client, AttachedAsset, User]
cannot :assign_roles, User
end
if user.has_role? :management
can :manage, [Property, Deed, Mortgage, Order, Owner, Client, AttachedAsset]
can :read, User
can :manage, User, id: user.id
cannot :destroy, [Property, Order, Client, User]
end
if user.has_role? :analyst
can :manage, [Property, Deed, Mortgage, Order, Owner, Client, AttachedAsset]
can :manage, User, id: user.id
cannot :destroy, [Property, Order, Client, User]
end
if user.has_role? :it
can :manage, [Property, Deed, Mortgage, Order, Owner, Client, AttachedAsset]
can :manage, User, id: user.id
can :read, User
cannot :destroy, [Property, Order, Client, User]
end
if user.has_role? :client
can [:read, :update], Client, id: user.client_id
can [:read, :update, :client_order_views], [Property, Order], client_id: user.client_id
can [:read, :update], Owner
can :create, [Property, Order, Owner]
can :manage, User, id: user.id
end
end
end