CanCan authorization issue index action - ruby-on-rails

I am having an issue trying to use the cancan authorization. I have users with one role per user, so I added user.role column as suggested in the cancan wiki.
I also have an index view for kindergardens and another index view for children.
What I am trying to achieve is to only allow access to the index views to the corresponding users with role kindergarden or parent (for children).
When I now authorize Kindergardens in kindergardens controller nobody is allowed to access the index view, although I defined it in my ability model.
Help would be much appreciated. I have no idea what I am missing...
Ability model:
class Ability
include CanCan::Ability
def initialize(user)
[...]
elsif user.role == 'kindergarden'
can [:update, :destroy], Kiga do |kiga|
kiga.user_id == user.id
end
can :read, Kiga do |kiga|
kiga.user_id == user.id
end
can :index, Kiga
can :create, Kiga
else
end
Kindergardens controller:
class KigasController < ApplicationController
before_action :set_kiga, only: [:show, :edit, :update, :destroy]
# GET /kigas
# GET /kigas.json
def index
#kigas = Kiga.all
authorize! :index, #kiga
end

You define your abilities as blocks and that needs to have instances. In index actions you have only Active Record scopes, not actual instances.
Btw you have a typo in controller code:
def index
#kigas = Kiga.all
authorize! :index, #kigas # was #kiga that is nil probably
end
See the docs:
The block is only evaluated when an actual instance object is present. It is not evaluated when checking permissions on the class (such as in the index action).
In this case you can use hashes of conditions
can [:update, :destroy], Kiga, user_id: user.id

Got solution
def index
#kigas = Kiga.all
authorize! :index, Kinga
end

Related

Cancancan ActiveAdmin: skip authorization for :create

I'm using Cancancan for authorization in ActiveAdmin.
Everything work fine except the :create. When create a new admin, cancancan will check is admin_user.id = id. However, ActiveAdmin make id = nil, so I can't create a new admin.
include CanCan::Ability
def initialize(admin_user)
can :manage, AdminUser, id: admin_user.id
....
end
end
My solution is everyone can skip authorization for create.
My application controller:
class ApplicationController < ActionController::Base
load_and_authorize_resource
skip_authorize_resource :only => :new
end
but it does nothing. Please help!
You can specify what actions need authorization like this...
def initialize(admin_user)
can [:edit, :destroy], AdminUser, id: admin_user.id
end
Just replace/add to [:edit, :destroy] with whatever actions you need that authorization for.
If you want all admin users to be able to perform an action on AdminUser...
can :some_action, AdminUser

cancancan authorize_resource not working as expected

I am getting an unexpected behaviour for a simple cancancan authorization.
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
if user.is_admin?
can :manage, :all
elsif user.is_standard?
can :manage, ServiceOrder, {user_id: user.id}
can :manage, ServiceOrderDetail, :service_order => { :user_id => user.id }
end
service_order.rb controller (partially shown)
class ServiceOrdersController < ApplicationController
authorize_resource
def show
#service_order = ServiceOrder.includes(:service_order_details).find(params[:id])
end
end
This does not work, as it lets the controller show ANY service_order record, instead of just those owned by the current_user.
The only way that this works is if I manually authorize the controller adding:
authorize! :show, #service_order
like this:
def show
#service_order = ServiceOrder.includes(:service_order_details).find(params[:id])
authorize! :show, #service_order
end
which makes no sense since authorize_resource is supposed to be doing that.
What is happening is the authorize_resource is happening before the show action, and since the #service_order is not set yet, it is checking against the class, and the user does have access to show a ServiceOrder just under constraint.
Adding authorize_resource will install a before_action callback that calls authorize!, passing the resource instance variable if it exists. If the instance variable isn't set (such as in the index action) it will pass in the class name. For example, if we have a ProductsController it will do this before each action.
authorize!(params[:action].to_sym, #product || Product)
from Cancancan documentations
What you will need to do is load_and_authorize_resource as suggested by widjajayd. or (if you do not want to use the cancancan default load action) do a before_filter that loads the resource manually using your custom method before the authorize_resource call.
my suggestion: instead using authorize_resource you using load_and_authorize_resource, and below is the sample for your controller
just make sure your strong_parameters declaration :service_order_params
load_and_authorize_resource param_method: :service_order_params

Is there any way to access the parent object in a Cancan nested resource ability?

I have a nested resource for which I'm using Cancan to do authorization. I need to be able to access the parent object in order to be able to authorize the :index action of the child (since no child instance is passed for an :index action).
# memberships_controller.rb
class MembershipsController < ApplicationController
...
load_and_authorize_resource :org
load_and_authorize_resource :membership, through: :org
..
end
ability.rb
can [:read, :write], Membership do |membership|
membership.org.has_member? user
end
This doesn't work for the :index action
Unfortunately the index action doesn't have any membership instance associated with it and so you can't work your way back up to check permissions.
In order to check the permissions, I need to interrogate the parent object (the org) and ask it whether the current user is a member e.g.
# ability.rb
...
can :index, Membership, org: { self.has_member? user }
Cancan almost lets me do this...
Cancan states that you can access the parent's attributes using the following mechanism:
https://github.com/ryanb/cancan/wiki/Nested-Resources#wiki-accessing-parent-in-ability
# in Ability
can :manage, Task, :project => { :user_id => user.id }
However this just works by comparing attributes which doesn't work for my case.
How can I access the parent object itself though?
Is there any way to access the parent object itself within the permissions?
I recently faced the same problem and ended up with the following (assuming you have Org model):
class MembershipsController < ApplicationController
before_action :set_org, only: [:index, :new, :create] # if shallow nesting is enabled (see link at the bottom)
before_action :authorize_org, only: :index
load_and_authorize_resource except: :index
# GET orgs/1/memberships
def index
#memberships = #org.memberships
end
# ...
private
def set_org
#org = Org.find(params[:org_id])
end
def authorize_org
authorize! :access_memberships, #org
end
end
ability.rb:
can :access_memberships, Org do |org|
org.has_member? user
end
Useful links
https://github.com/ryanb/cancan/issues/301
http://guides.rubyonrails.org/routing.html#shallow-nesting
Can't you do something like this?
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :index, Membership, org: {id: user.memberships.map(&:org_id)}
end
end

CanCan Auth issue

Good Afternoon All,
I am trying to sort out my user authentication and causing myself headaches.
I have a :role_type defined in User and my user has two roles, Employer or Developer, now I my user is currently developer and should be able to see jobs#index but it cannot and I get the default cancan message of unauthorized:
class JobsController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!
before_action :set_job, only: [:show, :edit, :update, :destroy]
and here is the Ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :create, :delete, :update, Job if user.role_type == "Employer"
can :read, Job if user.role_type == "Developer"
end
end
Thanks for the help.
As written in the comments: Make sure that you are not having a typo like "Employer" instead of "employer" :-)
I'm assuming the error occurs when the user.role_type == 'Employer'
Looking at your code it looks like the Employer is missing the :read action.
If Employer can manage all jobs (basic crud actions) you might want to consider giving him the :manage ability
Example:
can :manage, Job if user.role_type == "Employer"
can :read, Job if user.role_type == "Developer"
The :manage ability gives the user access to every action within the JobsController. See the following doc for clarification on the :manage ability: https://github.com/ryanb/cancan/wiki/defining-abilities#the-can-method
Otherwise you would need to add the :read action to Employer, as they are not a Developer and will not get the :read action

Rails 3 If Statement using a variable from a controller to another controller (ability.rb)

I'm using the Rails Plugin CanCan to handle permissions checks.
I have the following in ability.rb:
def initialize(user, projectid_viewing)
user ||= User.new
if projectid_viewing == 8
can :manage, :all
else
can :read, :all
end
end
The projectid_viewing is being sent from:
class ProjectsController < ApplicationController
before_filter :prepareCanCan, :only => [:show, :edit]
def prepareCanCan
#project = Project.find(params[:id])
projectid_viewing = #project.id
end
I have the 8 hardcoded above for testing purposes. and for some reason it isn't working at the if statement, did I do that statement incorrectly? It's always allowing for can: manage
I have the Project's controller logging, so I know that the value the controller is setting to projectid_viewing is 8.
Ideas?
I'm trying to understand… Depending on the project, all user can edit it, or can edit all models? If it's the project only, I would try:
def initialize(user)
user ||= User.new
can :manage, Project do |project|
project.id == 8
end
end

Resources