rails_admin+cancan2 unauthorized access in dashboard - ruby-on-rails

Using rails_admin + cancan2 i have a problem with the ability.
according with the official docs https://github.com/sferik/rails_admin/wiki/CanCan i have configured my ability.rb file:
class Ability
include CanCan::Ability
def initialize(user)
can :read, :all
if user
if user.has_role? :admin
can :access, :all
end
if user.has_role? :manager
can :access, :rails_admin # grant access to rails_admin
can :dashboard # grant access to the dashboard
end
end
end
end
the problem is using cancan version 1.6 works fine, but using cancan 2 the "manager" is unauthorized to access in the dashboard, but he is authorized to access in rails admin. So:
can :access, :rails_admin #work
can :dashboard #don't work
if i go in localhost:3000/admin the error is the classic
CanCan::Unauthorized in RailsAdmin::MainController#dashboard
but if i go localhost:3000/admin/models it works, so the
can :dashboard #don't work
doesn't works
can you help me?

can :dashboard, :all
should work.
After displaying dashboard, you need another patch for work with CanCan 2.0
# patch for CanCan 2.0
module RailsAdmin
module Extensions
module CanCan
class AuthorizationAdapter
def authorize(action, abstract_model = nil, model_object = nil)
#controller.current_ability.authorize!(action, model_object || abstract_model && model_name(abstract_model.model)) if action
end
def authorized?(action, abstract_model = nil, model_object = nil)
#controller.current_ability.can?(action, model_object || abstract_model && model_name(abstract_model.model)) if action
end
private
def model_name(model)
model.to_s.underscore.pluralize.to_sym
end
end
end
end
end

I've never seen something like
can :dashboard
The normal structure of the can directive is "can :action, :object" (or class)
So, from what I understand from your question, I assume you should change this into
can :manage, :dashboard
if you want to assign "all rights"

Related

I can only access rails_admin via cancancan with `can :manage, :all `

On rails 5. I'm trying to integrate cancancan 3.0.1 with rails_admin (1.4.2) and for the most part it works. My problem is that I can only get routing acccest to work if I give the user can :manage, :all otherwise I get an access Denied from cancancan
Here's my cancancan ability code. In this case the super admin gains access whereas the normal admin with can :manage, Project does not:
class Ability
include CanCan::Ability
def initialize(user)
can :read, :all # allow everyone to read everything
return unless user && user.admin?
can :access, :rails_admin
can :read, :dashboard # allow access to dashboard
if user.superadmin?
can :manage, :all # allow superadmins to do anything
elsif user.admin?
can :manage, Project
end
end
end
Is there anyway to allow users onto this route without manage :all?

Cancancan Cant get it work with User and Admin

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 .

How do I setup my CanCanCan permissions correctly?

I am a little confused about how to configure CanCanCan properly.
For starters, do I have to add load_and_authorize_resource to every controller resource I want to restrict access to?
This is what I would like to do:
Admin can manage and access all controllers and actions
Editor can read all, manage :newsroom, and can manage all Posts
Member can read every Post and can create & update Posts (not edit/delete/anything else), cannot access the newsroom. The difference between an update & edit post in our business rules is that an update is creating a new post that is a child post of the current post. So it isn't an edit. Just a new record with an ancestry association.
Guest can read every Post, but cannot create Posts nor access the Newsroom.
This is what my ability.rb looks like:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
#Admin
if user.has_role? :admin
can :manage, :all
can :manage, :newsroom
# Editor
elsif user.has_role? :editor
can :read, :all
can :manage, :newsroom
can :manage, Post
#Member
elsif user.has_role? :member
can :read, :all
can :create, Post
can :status, Post
can :update, Post do |post|
post.try(:user) == user
end
#Guest
else
can :read, :all
can :create, Post
can :status, Post
end
end
end
In my routes.rb I have this:
authenticate :user, lambda { |u| u.has_role? :admin or :editor } do
get 'newsroom', to: 'newsroom#index', as: "newsroom"
get 'newsroom/published', to: 'newsroom#published'
get 'newsroom/unpublished', to: 'newsroom#unpublished'
end
What is happening though, is when I am logged in with a user that has not been assigned any roles (i.e. what I want to be a "Guest"), they can access the Newsroom.
When I try to edit a post with the role of :member, it gives me a "Not authorized to edit post" error (which is correct).
I just can't quite lockdown the Newsroom and I am not sure why.
You do not need to use load_and_authorize_resource in every controller. That is a convenience macro that does two things. First it assigns an instance variable with the record(s) assumed for the current controller and action. It then authorizes the resource. For some controller actions the first step might be wrong, so you want to load your resource and then authorize it manually. An example from the Railscasts episode about CanCan is like this:
def edit
#article = Article.find(params[:id])
unauthorized! if cannot? :edit, #article
end
You can also do it like in the example on the CanCan Wiki for authorizing controllers:
def show
#project = Project.find(params[:project])
authorize! :show, #project
end
Or you can just use authorize_resource and take care of loading it yourself. In the end, you must make sure that CanCan is used for authorization somehow (controller macro or in each action). Regarding your abilities, I think you want something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
#Admin
if user.has_role? :admin
can :manage, :all
# Editor
elsif user.has_role? :editor
can :read, :all
can :manage, :newsroom
can :manage, Post
#Member
elsif user.has_role? :member
can :read, :all
can :create, Post
can :status, Post
can :update, Post do |post|
post.try(:user) == user
end
#Guest
else
can :read, :all
cannot [:index, :published, :unpublished], :newsroom
end
end
end
And here's an example like how you might be able to authorize your newsroom:
class ToolsController < ApplicationController
authorize_resource :class => false
def show
# automatically calls authorize!(:show, :tool)
end
end
A last personal note about CanCan is that I wouldn't suggest it for new projects since it isn't actively maintained anymore and that I found it a bit counterintuitive when defining abilities. That said, CanCan is one of the most well-documented gems that I have worked with, especially the wiki has loads of examples and explanations.
can :read, :all
means user has permission to read all the resources of your app. It should be
can :read, Post
also add
cannot :manage, :newsroom
where you do not want access to newsroom. The order in which you specify permissions matters.
As others have already mentioned, 'load_and_authorize_resource' is optional. Only 'authorize resource' is needed to authorize all actions of a controller. If you skip these then you can 'authorize' individual controller actions.
Avoid using block for ability unless absolutely necessary. For instance if Post has a user_id in it then you could do
can :update, Post, user_id: user.id
Lastly, 'class => false' is used where you do not have a model backing your controller.
i.e you do not have a model called 'Newsroom' but you have a controller called 'NewsroomsController'.
For what it's worth, I had to setup my NewsroomController like this:
class NewsroomController < ApplicationController
authorize_resource :class => false
This is what the working version of my ability.rb looks like after I got it to work with the permissions I needed:
#Roles
#Admin
if user.has_role? :admin
can :manage, :all
# Editor
elsif user.has_role? :editor
can :manage, :newsroom
can :manage, Post
#Member
elsif user.has_role? :member
can [:read, :create, :status], Post
can :update, Post do |post|
post.try(:user) == user
end
#Guest
else
can [:read, :status], Post
end
For starters, do I have to add load_and_authorize_resource to every controller resource I want to restrict access to?
Yes.
What is happening though, is when I am logged in with a user that has
not been assigned any roles (i.e. what I want to be a "Guest"), they
can access the Newsroom.
From the guest role above:
...
#Guest
else
can :read, :all
can :create, Post
can :status, Post
end
This gives a guest read access to everything and the ability to create posts.
If you want your Guests to only be able to read posts it should be:
...
#Guest
else
can :read, Post
# can :status, Post # maybe you want this aswell
end

Active Admin: can't scope the resource to current_admin_user

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

CANCAN undefined method `user_type?' for "admin":String

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

Resources