CanCan Ability in Rails - Giving partial manage - ruby-on-rails

I have three roles of AdminUsers:
admin
editor
student
I need the editor to have the ability to manage(add/view/delete) AdminUser accounts that its role is student.Yet, he can't see whose role is admin or editor.
With the following code, he could view only without creation/deletion/edit
if user.role?(:admin)
can :manage, :all
elsif user.role?(:editor)
can :read, ActiveAdmin::Page, :name => "Dashboard"
can :manage, :all
cannot :manage, AdminUser
can :manage, AdminUser, :role => :student
else
can :read, :all
end

If I understand you correctly, you're saying that a user who is an editor should be able to manage the account of another user who is a student. If that's the case, this should work:
if user.role?(:admin)
can :manage, :all
elsif user.role?(:editor)
can :read, ActiveAdmin::Page, :name => "Dashboard"
can :manage, AdminUser, :role => :student
else
can :read, :all
end

Related

Allow creation of a limited user only with CanCanCan

I want the Admin user to not have the ability to create users with the role of Super Admin but still be able to create other Admins and Regular Users. How do I accomplish this? Here is my Ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
if user.super_admin?
can :manage, :all
elsif user.admin?
can :manage, [Article, Comment]
can [:destroy, :update], User, :role_id => 2 # If Admin
can [:destroy, :update], User, :role_id => 3 # If User
can :read, User
can :create, User
elsif user.user_regular?
#cannot :read, ActiveAdmin::Page, :name => "Dashboard"
#can :manage, :all
end
end
end
Use cannot with in admin block like cannot :creat, User, :role_id => 1 # let 1 is super admin role id. You can get more info about combine ability at here

Rails 4: CanCanCan abilities with has_many :through association

I have a Rails app with the following models:
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
end
class Calendar < ActiveRecord::Base
has_many :administrations
has_many :users, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
For a given calendar, a user has a role, which is define in the administration join model.
For each calendar, a user can have only one of the following three roles: Owner, Editor or Viewer.
These roles are currently not stored in dictionary or a constant, and are only assigned to an administration as strings ("Ower", "Editor", "Viewer") through different methods.
Authentication on the User model is handled through Devise, and the current_user method is working.
In order to only allow logged-in users to access in-app resources, I have already add the before_action :authenticate_user! method in the calendars and administrations controllers.
Now, I need to implement a role-based authorization system, so I just installed the CanCanCan gem.
Here is what I want to achieve:
All (logged-in) users can create new calendars.
If a user is the owner of a calendar, then he can manage the calendar and all the administrations that belong to this calendar, including his own administration.
If a user is editor of a calendar, then he can read and update this calendar, and destroy his administration.
If a user is viewer of a calendar, then he can read this calendar, and destroy his administration.
To implement the above, I have come up with the following ability.rb file:
class Ability
include CanCan::Ability
def initialize(user, calendar)
user ||= User.new
calendar = Calendar.find(params[:id])
user can :create, :calendar
if user.role?(:owner)
can :manage, :calendar, :user_id => user.id
can :manage, :administration, :user_id => user.id
can :manage, :administration, :calendar_id => calendar.id
elsif user.role?(:editor)
can [:read, :update], :calendar, :user_id => user.id
can :destroy, :administration, :user_id => user.id
elsif user.role?(:viewer)
can [:read], :calendar, :user_id => user.id
can :destroy, :administration, :user_id => user.id
end
end
end
Since I am not very experimented with Rails and it is the first time I am working with CanCanCan, I am not very confident with my code and would like some validation or advice for improvement.
So, would this code work, and would it allow me to achieve what I need?
UPDATE: with the current code, when I log in as a user, and visit the calendars#show page of another user's calendar, I can actually access the calendar, which I should not.
So, obviously, my code is not working.
Any idea of what I am doing wrong?
UPDATE 2: I figured there were errors in my code, since I was using :model instead of Model to allow users to perform actions on a given model.
However, the code is still not working.
Any idea of what could be wrong here?
UPDATE 3: could the issue be caused by the fact that I use if user.role?(:owner) to check if a user's role is set to owner, while in the database the role is actually defined as "Owner" (as a string)?
UPDATE 4: I kept on doing some research and I realized I had done two mistakes.
I had not added load_and_authorize_resource to the calendars and administrations controllers.
I had defined two attributes two parameters — initialize(user, calendar) — instead of one in my initialize method.
So, updated both controllers, as well as the ability.rb file as follows:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role?(:owner)
can :manage, Calendar, :user_id => user.id
can :manage, Administration, :user_id => user.id
can :manage, Administration, :calendar_id => calendar.id
elsif user.role?(:editor)
can [:read, :update], Calendar, :user_id => user.id
can :destroy, Administration, :user_id => user.id
elsif user.role?(:viewer)
can [:read], Calendar, :user_id => user.id
can :destroy, Administration, :user_id => user.id
end
end
end
Now, when I try to visit a calendar that does not belong to the current_user, I get the following error:
NoMethodError in CalendarsController#show
undefined method `role?' for #<User:0x007fd003dff860>
def initialize(user)
user ||= User.new
if user.role?(:owner)
can :manage, Calendar, :user_id => user.id
can :manage, Administration, :user_id => user.id
can :manage, Administration, :calendar_id => calendar.id
How I can fix this?
There is no such method role? the User model. The Cancancan documentation is at fault for assuming such a method exists in the examples.
To fix this, you should instead do:
if user.role == 'Owner'
...
elsif user.role == 'Editor'
...
elsif user.role == 'Viewer'
...

With CanCan, how do i limit ability based on association / child attribute

I have a db setup where there are many users, which have roles of member or admin. Each user has many cars. Each car has many timeslips
So, how do i limit a user's ability to edit a Timeslip only if he is the owner of the parent car.
In CanCan:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :member
can :manage, Car, :user_id => user.id
can :manage, Timeslip, :car => {:user_id => user.id}
end
end
end
so the line can :manage, Timeslip, :car => {:user_id => user.id} is where i need some help.
Because Timeslip is an association/child of Car, i need to check that its parent car.user_id = the Cancan user.id
I thought how i wrote this is in line with the CanCan docs, but where have I gone wrong?
There might be a shorter way to write it, but this will work:
can :manage, Timeslip do |timeslip|
timeslip.car.user_id == user.id
end
This is what's working for me: can :manage, Timeslip, car: { roles: { id: user.role_ids } }

Cancan user permission?

I am using cancan for permissions and I would like it so users cannot see other users when visiting their profile/users page. User should only be able to see themselves.
In my ability.rb file I have
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? :rookie
can [:update, :destroy], [Album, Photo, User]
can :read, :all
end
can :manage, Album, :profile => { :user_id => user.id }
can :manage, Photo, :profile => { :user_id => user.id }
can :manage, Video, :profile => { :user_id => user.id }
can :manage, Comment, :blog => { :profile => { :user_id => user.id } }
can :manage, User, :id => user.id
end
end
In my users_controller I have
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
def index
#user = current_user
end
def show
#user = User.find(params[:id])
end
end
The above usually works but since user is the primary model I am not sure how I can resolve this. Rails gives me the error
undefined method `user_id'
It should be:
#ability.rb:
can :manage, User, :id => user.id
I am assuming that most users start with role :rookie and you have ability
can :read, :all
for users with role :rookie. Then this means that all :rookie users will be able to read
all resources.

Rails cancan authorizing nested resources

I have Projects resource which is nested in Users resource.
My Cancan Ability class is:
class Ability
include CanCan::Ability
def initialize(user)
#everyone
can :read, Project
if user.blank?
# guest user
...
else
#every signed in user
case user.role
when User::ROLES[:admin]
#only admin role user
can :manage, :all
when User::ROLES[:member]
#only member role user
can :update, User, :id => user.id
can [:create, :update, :destroy], Project, :user_id => user.id
else
end
end
end
end
And Projects controller:
class ProjectsController < ApplicationController
load_and_authorize_resource :user
load_and_authorize_resource :projects, :through => :user, :shallow => true
...
end
I have few questions:
Is it possible to deny :read User and allow to :read Project, so that everyone could access /users/10/projects, but not /users/10 or /users?
How can I deny user accessing :new action with other user_id? For example, if I add
#everyone
can :read, User
can :read, Project
this code allows user with id 42 to access /user/41/projects/new.
Solved it by doing:
class Ability
include CanCan::Ability
def initialize(user)
#everyone
can :read, Project
can :read, User # required to access nested resources
cannot :index, User
cannot :show, User
if user.blank?
# guest user
...
else
#every signed in user
case user.role
when User::ROLES[:admin]
#only admin role user
can :manage, :all
when User::ROLES[:member]
#only member role user
can :update, User, :id => user.id
can :manage, Project, :user => { :id => user.id }
else
end
end
end
end

Resources