Active Admin: can't scope the resource to current_admin_user - ruby-on-rails

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

Related

Rails - CanCanCan - common abilities

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

Restrict view for various roles in pundit

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

setting up user roles within cancan

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)

Setting roles for a users in rails with cancan

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...

Any lazy way to do authentication on RoR?

I want to make a simple login, logout, also, different user have different user role. The Restful authentication seems works great, and the cancan is also very sweet for controlling user ability. But the question is how can I let these two works together. I watched the railcast, I was whether how to detect the user ability? Do I need to add a "ability" column in the user table?? Thank u.
http://railscasts.com/episodes/67-restful-authentication
http://railscasts.com/episodes/192-authorization-with-cancan
Look at the CanCan GitHub page: http://github.com/ryanb/cancan
Based on looking at both that and the RailsCast, I notice two things:
You define Ability as a separate model. There doesn't appear to be any necessary database columns.
There is no way you are forced to do roles, you are free to do this however you will.
With restful_authentication, just do the normal thing with your User model.
The most natural way to add CanCan would be to add an extra column to your User model called role or ability or something, then define methods as you see fit. Personally I'd probably do some kind of number system stored in the database, such as "0" for admin, "1" for high-level user, "2" for low-level user, etc.
Here's a few possibilities:
# Returns true if User is an admin
def admin?
self.role == 0
end
And:
# Returns true if User is admin and role?(:admin) is called, etc.
def role?(to_match)
{
0 => :admin,
1 => :super_user,
2 => :user,
3 => :commenter,
}[self.role] == to_match
end
Then in your Ability initialize method, you can use some kind of conditionals to set abilities, such as these snippets from the Railscast/readme:
if user.role? :admin
can :manage, :all
elsif user.role? :super_user
...
end
Or:
if user.admin?
can :manage, :all
else
...
end
I wrote a simple solution that works with CanCan too, just add a role_id:integer column to the User model:
# puts this in /lib/
module RolePlay
module PluginMethods
def has_roleplay(roles = {})
##roles = roles
##roles_ids = roles.invert
def roles
##roles
end
def find_by_role(role_name, *args)
find(:all, :conditions => { :role_id => ##roles[role_name]}, *args)
end
define_method 'role?' do |r|
r == ##roles_ids[role_id]
end
define_method :role do
##roles_ids[role_id]
end
end
end
end
then include this line in config/initializers/roleplay.rb
ActiveRecord::Base.extend RolePlay::PluginMethods
finally use it in your User model:
class User < ActiveRecord::Base
# ...
has_roleplay(:admin => 0, :teacher => 1, :student => 2)
# ...
end
now your model will have 2 new methods:
#user.role?(:admin) # true if user has admin role
#user.role # returns role name for the user

Resources