I followed CanCanCan's configuration instructions for Rails Admin. I get the error message below:
CanCan::AccessDenied in RailsAdmin::MainController#dashboard
You are not authorized to access this page.
Extracted source (around line #180):
178 if cannot?(action, subject, *args)
179 message ||= unauthorized_message(action, subject)
180 raise AccessDenied.new(message, action, subject, args)
181 end
182 subject
183 end
ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :read, :all
can :manage, Article, user_id: user.id
return unless user.admin_role?
can :access, :rails_admin
can :read, :dashboard
can :manage, :all
end
end
rails_admin.rb:
RailsAdmin.config do |config|
## == CancanCan ==
config.authorize_with :cancancan
config.actions do
dashboard # mandatory
index # mandatory
new
export
bulk_delete
show
edit
delete
show_in_app
end
end
Gemfile includes these:
gem 'cancancan'
gem 'rails_admin', '~> 2.0', '>= 2.0.2'
Permissions seem to be working OK everywhere else though. There are 2 possible roles for a user: admin or user. On my admin account, I'm recognized as an admin and can do things users cannot. For example:
app/views/articles/index.html.erb:
<% if can? :update, article %><td><%= link_to 'Edit', edit_article_path(article) %></td> <% end %>
The "Edit" option only shows up for the user who created the article OR an admin. This works as expected.
In config/initializers/rails_admin.rb, removing config.authorize_with :cancancan and adding the code below seemed to solve the problem. Users with the admin_role can visit /admin, but others cannot. I'm still not sure why cancancan was not playing nicely, however this is now working as expected.
rails_admin.rb:
#config.authorize_with :cancancan
config.parent_controller = "::ApplicationController"
config.authorize_with do
if !current_user || !current_user.admin_role?
redirect_to(main_app.root_path, alert: "You are not permitted to view this page")
end
end
Thanks to philtr
As a note I am using Rails 6.0.2.2; Rails Admin 2.0.2; Cancancan 3.1.0; and I am NOT using Clearance.
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 .
I am trying to use cancancan for authorization. When I am an authorized 'group_creator' user I am still denied access to groups/new.
Roles are functional and has_role? from rolify works. So I think the issue comes with cancancan or maybe devise.
Ability.rb
def initialize(user)
user ||= User.new # guest user (not logged in)
alias_action :create, :read, :update, :destroy, to: :crud
if user.has_role? :group_creator
can :create, Group
elsif user.has_role?(:creator, Group)
can :manage, Group, owner_id: user.id
elsif user.has_role?(:admin, Group)
can :crud, Group, :id => Group.with_role(:admin, user).pluck(:id)
else
can :read, Group
end
end
Relevant GroupsController.rb
class GroupsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource
def index
#groups = Group.all # Can be deleted due to cancancan?
end
def new
#group = Group.new # Can also be deleted due to cancancan...
end
end
I have tried debugging in the console with:
user=User.last
user.has_role? :group_creator # returns true
group=Group.last
ability=Ability.new(user)
ability.can?(:create, Group) # returns false
ability.can?(:create, group) # returns false
Two things here.
The user needs :read AND :create ability to access new action. This solved this problem for me.
After fiddling with a fresh mind this morning some advice for others running across this that are new like me... Make sure to reboot server after altering ability.rb... (I am not sure if this was part of the issue yesterday but I thought I should mention it). Cancancan wiki has some explanation on how to utilize a separate permissions table for dynamic modification of abilities.
After installing and setting up devise for user login I went ahead and added the rails_admin gem. Now if I go too localhost:3000/admin I can access the admin dashboard but How do I add admin authentication so you would need to enter admin credentials to access the admin dashboard?
I would recommend you to use an authorization gem called cancancan (is the updated version of cancan) it's super easy to use and it will let you to give certain permissions to different kind of users. If you don't know nothing about this gem i will recommend you to see this railscasts that will teach you how to use it properly.
So after you installed the cancancan gem in the ability.rb file you just need to do something like this to limit the admin access
models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user && user.admin?
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard
can :manage, :all
else
can :read, :all # allow everyone to read everything
end
end
end
And don't forget to tell to the rails_admin gem that you are using cancancan to validate the authorization
config/initializers/rails_admin.rb
RailsAdmin.config do |config|
## == Cancan ==
config.authorize_with :cancan
end
To user the "user.admin?" method you must create it into the user model, but it will only work if you have a role model that has_many users and users belongs_to role otherwise you will need other way to verify the role, so it will be something like this
models/role.rb
has_many :users
models/user.rb
belongs_to :role
def admin?
role_id == 0 # If you have id == 0 for admin
end
Also i will recommend you to use a role model or enum to manage the different roles with ease.
I hope it helps :D
In your routes.rb
authenticate :admin do
mount RailsAdmin::Engine => 'rails_admin', as: :rails_admin
end
Provide your user with an admin role such that you can test, e.g. user.admin?.
Configure rails_admin to inherit from your application controller and to catch any access other than a signed in admin user.
#config/initializers/rails_admin.rb
RailsAdmin.config do |config|
...
config.parent_controller = 'ApplicationController'
config.authenticate_with do
# current_user = User.find_by_id(session[:user_id])
raise InvalidAdminAccess, 'Access denied.' unless current_user.try(:admin?)
end
end
Define the InvalidAdminAccess error class in
# app/errors/invalid_admin_access.rb
class InvalidAdminAccess < StandardError
end
Catch the error in your application controller
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
rescue_from InvalidAdminAccess do |exception|
respond_to do |format|
format.json { head :forbidden, content_type: 'text/html' }
format.html { redirect_to main_app.root_url, alert: exception.message }
format.js { head :forbidden, content_type: 'text/html' }
end
end
end
In this way, when there is incorrect access, rails_admin triggers an exception that is caught and handled by the application controller.
So I've implemented the rails admin gem, and even with the cancan gem, I can't figure out a way to password protect localhost:3000/admin
Could someone give me a step by step guide for doing this? I can't really find a view or controller for the admin panel, so I'm not sure how to password protect it.
This page describes how you should be using Cancan : https://github.com/sferik/rails_admin/wiki/CanCan
# in config/initializers/rails_admin.rb
RailsAdmin.config do |config|
config.authorize_with :cancan
end
Their ability.rb example may be a bit more than you need this is mine :
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
can :access, :rails_admin # grant access to rails_admin
can :dashboard # grant access to the dashboard
end
end
end
You must then give the admin role to a user.
you can do this at the rails console as so :
user = User.find(1) #find user with ID 1
user.add_role :admin #assign role
user.has_role? :admin #should evaluate to True
I've installed CanCan and added line gem 'cancan' to Gemfile. Ran bundle.
I created app/models/ability.rb with code:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
end
end
end
ActiveAdmin and Devise are installed and plugged.
I added if cannot? to app/admin/codes.rb:
ActiveAdmin.register Code do
if cannot? :manage, Code
flash[:notice] = "Access denied"
redirect_to '/'
end
index do
column :id
column :title
column :author
column :created_at
column :updated_at
column :language
default_actions
end
end
rails cannot start server or open rails console:
app/admin/codes.rb:2:in `block in <top (required)>': undefined method `cannot?'
for #<ActiveAdmin::ResourceDSL:0x4ae7fe8> (NoMethodError)
How can I fix it?
The cancan methods are added to all controllers and models and not anything else, so you will need to manually include the cancan methods in your ActiveAdmin class. Consider following the pattern here:
https://github.com/ryanb/cancan/blob/master/lib/cancan/controller_additions.rb
with something like include CanCan::ControllerAdditions
I did it.
I have appended code that executes into controller context:
controller do
before_filter :check_admin
def check_admin
if cannot? :manage, Code
flash[:alert] = "Access denied!"
redirect_to '/admin'
end
end
end