I am working on a rails application and it requires two different types of roles. One is Employee and other is Admin.
Cancan documentation says that it assumes there is a user or current_user method in the application.
So how can I use cancan to set roles for employee and manager in my app ?
Do like this
write this in application helper
def is_employee?(user)
emp_role = Role.find(:first, :conditions => ["name = ?", "Employee"])
return user.roles.include?(emp_role)
end
def is_admin?(user)
admin_role = Role.find(:first, :conditions => ["name = ?", "Admin"])
return user.roles.include?(admin_role)
end
And abily look like this
class Ability
include CanCan::Ability
include ApplicationHelper
def initialize(user)
# Define abilities for the passed in user here. For example:
#
user ||= Employee.new # guest user (not logged in)
if is_admin?(user)
can :manage, :all
# cannot :manage, IpAddress
elsif is_employee?(user)
#your code
end
For define roles see it
http://stackoverflow.com/questions/10222400/rails-adding-an-admin-role-using-devise-who-can-see-all-the-users/10222813#10222813
It sure works...
Related
I am using Rails 4, devise, Role Model and CanCanCan.
Is it possible to define an ability in ability.rb that is common to a number of roles?
For example, every logged in user can CRUD their own profile page? And then roles have specific abilities on top of that common ability?
How does that work? Do I need to create a role in Role Model for common abilities and then allow each user to have multiple roles, so that they get the common abilities as well as the role specific abilities?
For example, in my ability.rb, I have:
class Ability
include CanCan::Ability
def initialize(user)
alias_action :create, :read, :update, :destroy, :to => :crud
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
#users who are not signed in can create registration or login
# can read publicly available projects, programs and proposals
can :read, Project, {:active => true, :closed => false, :sweep => { :disclosure => { :allusers => true } } }
# {:active => true, :closed => false && :Project.sweep.disclosure.allusers => true}
# if user role is student
if user_signed_in?
can :crud, Profile, :user_id => user.id #[for themselves]
elsif user.try(:profile).present? && user.profile.has_role?(:student)
So, I want students to be able to read the same things that guests can read. Is there a way to say that students can do everything that new users and users who are signed in can do (as well as the student specific abilities)?
You can make a kind of composition in your roles through function calls like this
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)
#users who are not signed in can create registration or login
# can read publicly available projects, programs and proposals
# {:active => true, :closed => false && :Project.sweep.disclosure.allusers => true}
# if user role is student
if user_signed_in?
if user.try(:profile).present? && user.profile.has_role?(:student)
student
else
authenticated
end
else
anonymous
end
end
def anonymous
can :read, Project, {:active => true, :closed => false, :sweep => { :disclosure => { :allusers => true } } }
end
def authenticated
anonymous
can :crud, Profile, :user_id => user.id #[for themselves]
end
def student
authenticated
#other student abilities
end
#other roles follow the same principal
def teacher
authenticated
end
end
The authenticated function will contain the common abilities for any role and each role that needs it will just call (it's kind of inheritance where any student can do what the authenticated user can plus his abilities)
I have here add an example ability class for your understanding. You can understand easily the code and read comments. Your code seems not good, I can point one thing, you should not manage role through profile, you should use user for assign or manage roles.
If you want to give same ability to a group of user then you can use this type of || condition user.has_role?(:role_one) || user.has_role?(:role_two) and pass ability block as can :manage, [SomeClassName, SomeClassName].
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
#Only same user can mange his Profile
can :manage, [Profile], :user_id => user.id
#Give rule wise permission
if user.admin?
can :manage, :all
elsif user.has_role?(:some_role_name)
can :manage, [SomeClassName]
elsif user.has_role?(:role_one) || user.has_role?(:role_two)
can :manage, [SomeClassName, SomeClassName]
else
can :read, :all
end
end
end
Hope this will help you complete your task.
I am using this https://github.com/ryanb/cancan/wiki/Role-Based-Authorization#alternative-role-inheritance Works fine for me
My Ability file has
if user.has_role? :admin
can :manage, :all
else
can :manage, Company, :id => Company.with_role(:operator, user).pluck(:id)
...
end
And my Company Controller index has
def index
#companies = Company.with_role(:operator, current_user)
But when I sign in as a User who operates a company, I cannot access that page. (Even though the Company.with_role(:operator, user) returns a relation in the console!)
Companies have Codes. I am not sure how to write this in cancan:
Company.with_role(:operator, user).map{|o| o.codes}
But the wiki says if I use a block then authorize_resource will not set the instance variable #codes, because it doesn't know which objects belong to the user. So I cannot use:
can :manage, Code => do |Code|
user.has_role? :operator, code.company
end
I am looking for a solution that will let my CodesController do:
def index
if params[:company_id]
#keywords = Code.where(:company_id => params[:company_id])
end
And otherwise show the user all their Codes across all of the Companies they have the operator role for.
How about:
can :manage, Company do |company|
user.has_role? :operator, company
end
can :manage, Code do |code|
user.has_role? :operator, code.company
end
If you didn't use block syntax for the can definition you could use load_and_authorize_resource in your CodesController to filter the index to only those that are accessible to the current user.
Update
Because this used the block syntax CanCan can't use determine which objects to load with load_resource (since it wants to use SQL syntax). If you can rewrite it to not use a block syntax then you'll be good. If you have to use the role type logic you can add code similar to the following in your index method in your controller:
#codes = Code.all.select {|code| can?(:manage, code)}
or if you want to bypass the ability in this case for efficiency
#codes = Company.with_role(:operator, #current_user).codes
def index
if params[:company_id]
#codes = Company.with_role(:operator, current_user).where(:id => params[:company_id]).map{|o| o.keywords}.flatten
else
#codes = Company.with_role(:operator, current_user).map{|operator| operator.keywords}.flatten
end
end
There is only admin interface in my app and I use AdminUser model. The admin users can have different roles.
I want to change the resource retrieval based on admin role. I added to my ActiveAdmin register block:
#app/admin/payments.rb
scope_to :current_admin_user
And I expect I could write something like:
#app/models/admin_user.rb
def payments
case self.role
when role == 'manager'
Payments.where('...')
when role == '...'
end
end
But this doesn't work and always shows all the resources.
Any idea how can I get this work?
Finally, I used the scoped_collection method
ActiveAdmin.register Payment do
...
controller do
def scoped_collection
#roles which need to be scoped
if current_admin_user.role == 'accountant' or current_admin_user.role == 'manager'
resource_class.send(current_admin_user.role)
end
end
end
And then just define the scopes in the model:
class Payment < ActiveRecord::Base
scope 'accountant', where('...')
scope 'manager', where('...')
...
end
I found the better solution, then.
Authorization adapter do just fine, and there is not need for scopes. For example, with CanCan:
class Ability
include CanCan::Ability
def initialize(user)
#read/manage actions
if user.role == 'manager'
can :manage, Payment, :accessible_by_manager => true
elsif user.role == 'accountant'
can :read, Payment, :accessible_by_accountant => true
else
#can read all
can :read, Payment
end
end
end
I am new to Rails and am having troubles figuring out how to create user roles and then have it working with Cancan. I am following rails cast on this and the cancan wiki. What I don't understand to do is define each user role, for example what the admin, registered member and guest can access. I'm not sure if I'm on the right path or not with my coding. I have also run into a "undefined local variable or method `roles_mask'" error.
I don't believe I have the roles set proper in the below file. I have created user authentication from scratch if that helps any. The sections I have so far are galleries and user profile. ATM if I create a new account and select drop down box option "admin", I don't have any admin powers. I am still lock out of accessing pages.
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if #user && #user.role?(:admin)
can :manage, :all
else
can :read, :all
end
if user.role? :user
can :manage, Profile
end
if user.role? :admin
can :manage, Profile
end
end
end
user.rb
ROLES = %w[admin user guest banned]
def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
end
def roles
ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
end
def role?(role)
roles.include? role.to_s
end
In your ability.rb file,
if #user && #user.role?(:admin)
should probably be
if user && user.role?(:admin)
here is my problem:
I'm using cancan gem. I think all is set up well, except for one thing, that produces undefined method 'user_type?' for "admin":String.
Here is my ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.user_type? :admin
can :manage, :all
# [code code code code commented to find more easily the problem]
else
can :read, :all
end
end
end
My user.rb
require 'digest/sha2'
class User < ActiveRecord::Base
#definisco i ruoli
ROLES = %w[admin client banned]
default_scope :order => 'user' [...]
I can't figure out what I have to modify. Why if user.user_type? :admin don't check the current user attribute? Thanks a lot for your attention!
UP: Ok, I read around that cancan need method "current_user" declared in application controller. Now I work on it. News, soon.
You should do like this.I think it is better approach.
Seed roles into DB.
And in application helper paste this code.
def is_admin?(user)
admin_role = Role.find(:first, :conditions => ["name = ?", "admin"])
return user.roles.include?(admin_role)
end
And then in ability call like
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if is_admin?(user)
can :manage, :all
# [code code code code commented to find more easily the problem]
else
can :read, :all
end
end
end
And it will work fine.......