In my app I have the following models:
Users
Groups
Permissions (user_id, group_id, role_id)
Where role_id 1: admin, 2: member
I want to make sure I'm understanding CanCan correctly. In the ability.rb file, I only want group admins (permission.role_id == 1) to be able to update/destroy/create new group permissions.
permission.role_id == 2, members, should just be able to read the group and the group's permissions. Except for having the ability to destroy their group permission.
Here is my CanCan ability.rb file:
class Ability
include CanCan::Ability
def initialize(current_user, groupid_viewing)
current_user ||= User.new #Guest user (not signed in)
if groupid_viewing && current_user.try(:role, groupid_viewing) == 'Admin'
can :manage, Group
can [:create, :update], Permission do |permission|
current_user.try(:role, groupid_viewing) == 'Admin'
end
class GroupsController < ApplicationController
....
def current_ability
#current_ability ||= Ability.new(current_user, params[:group_id] && params[:group_id].to_i)
end
class ApplicationController < ActionController::Base
def current_ability
#current_ability ||= Ability.new(current_user, nil) #(user, group)
end
You also need to specify the abilities for the role_id:2.
if groupid_viewing && current_user.try(:role, groupid_viewing) == 'Member'
can :read, Group
can :destroy, Permission do |permission|
current_user.try(:role, groupid_viewing) == 'Member'
end
Also, there is no need of creating current_ability the way you are doing.
It should be an after_create callback that should assign abilities when the Member or Admin is created.
Related
So i am using Cancancan gem in my application.I have Users authenticated with the Devise/Omniauth gems and Admins that they are authenticated with a simple custom authentication.
I want to achieve
ability.rb
def initialize(userOrAdmin)
if userOrAdmin.user?
can :read, User
return unless user.present?
can :manage, User, id: user.id
elsif userOrAdmin.admin?
can [:update, :read] , Admin, id: admin.id
end
end
end
but that doesnt work.
I tried to override the ability method like that
application_controller
def current_ability
if current_admin?
#current_ability ||= Ability.new(current_admin)
elsif current_user?
#current_ability ||= Ability.new(current_user)
end
end
but i am getting a nomethod current_admin error probably because Cancancan assumes a current_admin from device but cant find it although i am using an current_admin method of my own.
I also tried to assign roles with the enum in both User.rb and Admin.rb and change ability.rb properly but i got an undefined method admin? for User error
Cancancan verion 2.0
After some searching, I found some helpful articles:
CanCanCan: Defining Abilities - Best Practicies
CanCanCan: CCC That Scales
CanCanCan: Refactoring Abilities
Rails: Routing From The Outside In
I suggest you try the following:
# Ability.rb
class Ability
include CanCan::Ability
def initialize(user)
# Everyone:
can :read, User
# Users:
return unless user.present?
can :manage, User, user_id: user.id
# Admins:
return unless user.admin?
can :manage, :all
end
end
# Routes.rb
devise_for :users # current_user:
devise_for :admins # current_admin:
# Application_Controller.rb
def current_ability
#current_ability ||= current_admin ? AdminAbility.new(current_admin) : UserAbility.new(current_user)
end
I found a solution that works
application_controller.rb
def current_ability
if current_user
return if current_admin.present?
#current_ability ||= Ability.new(current_user)
elsif current_admin
return unless current_admin.present?
#current_ability ||= Ability.new(current_admin)
end
end
end
ability.rb
class Ability
include CanCan::Ability
def initialize(userOrAdmin)
if userOrAdmin.is_a? User
can :read, User
can [:update, :read], User, id: userOrAdmin.id
elsif userOrAdmin.is_a? Admin
can :read, Admin
can [:update, :read], Admin, id: userOrAdmin.id
end
end
end
Althought this works without errors . Whenever i am signed in both like a user and admin the admin role appears CanCan not authorized error .
My two controllers user and web_cred are associated through userPass .
I applied cancancan gem but still unable to create a new web_cred ability class.
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :manage, WebCred, user_passes: {user_id: user.id}
end
My controller
load_and_authorize_resource :user
load_and_authorize_resource :web_cred
def create
#web_cred = WebCred.new(web_cred_params) # Not the final implementation!
if #web_cred.save
userpass = UserPass.new
userpass.user_id = current_user.id
userpass.web_cred_id = #web_cred.id
userpass.save
redirect_to home_index_path
else
render new
end
end
Your answer will be appreciated.
I am following up from a problem that I had before. I was able to get the code to work for three roles, but I need to include 4 roles in the mix.
The problem: I have 4 roles (user, business user, super user, and admin). Admins have access to everything (user index). Super users can only see both users and business users (user index).
The error: I have a functioning app that allows admins to have access to everything, but my super users can only see users (and not business users). I tried switching in the User Policy resolve method, for the super user to role: 'business_user' to see if that even worked. Well, it does not work and it only shows me users (not business_users). It's probably a simple ruby issue that I'm overlooking.
User Policy
class UserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#user = model
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
else user.super_user?
scope.where(role: 'user')
end
end
end
def index?
#current_user.admin? or #current_user.super_user?
end
end
User Controller
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = policy_scope(User)
authorize #users
end
[rest of the controller]
User Model
class User < ActiveRecord::Base
enum role: [:user, :business_user, :super_user, :admin]
[rest of model]
end
Can you try to change this method :
def resolve
if user.admin?
scope.all
else user.super_user?
scope.where(role: 'user')
end
end
By this :
def resolve
if user.admin?
scope.all
else user.super_user?
scope.where('role == "user" or role == "business_user"').all
end
end
You have to change your query to have both roles.
I figured out what I had to do. It was a two step process. First, I had to change the role to the numerical value that pundit stores it as instead of the string, so the role would be 0 & 1. Second, I used an array to feed them into the param so it would accept multiple options.
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
if user.admin?
scope.all
elsif user.super_user?
scope.where(role: [1,0])
else
scope.none
end
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)