cancan not working with activeadmin 0.4.4 - ruby-on-rails

I am not beeing able to make this work.
I have a project with activeadmin 0.4.4 and devise working, and I need to include role-based permissions to it, so I thought on CanCan. The roles could be 'administrator' or 'publisher', administrator can manage all and publisher only a post section.
I followed https://github.com/gregbell/active_admin/wiki/How-to-work-with-cancan-with-activeadmin, the AdminUser crud it is working fine, and the config.authentication_method = :authenticate_admin_user! was alredy uncomment.
In my ApplicationController I added the following:
# https://github.com/ryanb/cancan/wiki/exception-handling
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.html do
redirect_to admin_root_path, :alert => exception.message
end
end
end
# https://github.com/ryanb/cancan/wiki/changing-defaults
def current_ability
#current_ability ||= Ability.new(current_admin_user)
end
And here is my Ability class:
class Ability
include CanCan::Ability
def initialize(user)
user ||= AdminUser.new
if user.role?('administrator')
can :manage, :all
else
can :read, :all
end
end
end
The Ability initialize method is not executing. I tried abort, -as the comments suggest- puts or p, and even sintax error and I have nothing. What am I mising?
I'm stuck here, I also tried http://www.activeadmin.info/docs/13-authorization-adapter.html#using_the_cancan_adapter but it is rising undefined method "authorization adapter", I am not sure if this is working with my activeadmin version. And adding a require active_admin/cancan_adapter rise a LoadError.
Thanks.

Finally I did the trick thanks to http://makandracards.com/jan0sch/13877-rails-activeadmin-and-cancan
Basically I need to add a controller block in the activeadmin register page for loading it:
ActiveAdmin.register Whatever do
#...
controller do
load_and_authorize_resource :except => :index
def scoped_collection
end_of_association_chain.accessible_by(current_ability)
end
end
#...
end

Related

How do I get my ability.rb to work properly for my index action?

This is what my ability.rb looks like:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
end
can :manage, Connection, inviter_user_id: user.id
end
end
In my controller I have this:
class ConnectionsController < ApplicationController
load_and_authorize_resource
skip_authorize_resource only: :index
layout 'connections'
def index
#family_tree = current_user.family_tree
#inviter_connections = current_user.inviter_connections.order("updated_at desc")
#invited_connections = current_user.invited_connections.order("updated_at desc")
end
end
In my application_controller.rb, I have this:
rescue_from CanCan::AccessDenied do |exception|
redirect_to authenticated_root_url, :alert => exception.message
end
Yet, when I try to visit /connections when I am not logged in, I get this error:
NoMethodError at /connections
undefined method `family_tree' for nil:NilClass
Also, when I remove the can :manage, Connection from my ability.rb it actually sends me to my login page like I expect.
How do I get both to work?
It looks like you are using Devise for authentication. For this kind of validation when using devise you should add this to your controller:
before_action :authenticate_user!
Try the following:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
else
can :manage, Connection, inviter_user_id: user.id
end
end
end
Also, noticed that you are skip_authorize_resource only: :index, try commenting out that and see if it works.
On line 8 of your controller, current_user is nil when you're not logged in, and it's calling family_tree on it.
You need something like (just as an example, it depends on your needs):
#family_tree = current_user.try(:family_tree) || FamilyTree.new
The reason it "works" when you remove the line in Ability is because that removes the ability to see the connection, so the before_filter redirects before you ever get inside index. What's probably tripping you up is the Connection record has a inviter_user_id of nil, and User#id is nil, so it's giving you permission to get into index.
It could also happen if you forgot to put this at the top of the controller:
load_and_authorize_resource
See docs for more.

Rails gem: Cancan on production is not working

I'm using cancancan.. and it works fine on development env .. but once i got it on the production the site works like if there's no gem installed at all.. no authorization handled.
I can't figure out what is wrong
--------------------edited
in the application controller there's
before_filter :authenticate_user!, if: :devise_controller?
rescue_from CanCan::AccessDenied do |exception|
# Make sure we don't redirect to an HTML page
# in API requests because this is absolutely a bad idea
unless request.accept == 'application/json'
flash[:error] = "Access denied."
redirect_to root_url
end
end
and in the ability file
def initialize(user)
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :index, Book
can :index, Category
end
end

before_filter require_login creates an infinite loop

I'm new to ruby and Rails. I'm Using Devise and CanCan along with Rails_Admin in my app.
I'm trying to do -> If a user is already logged in, and it's an admin, redirect to rails_admin_path if it's not an admin but just a user then redirect to 'upload_path', if it's not logged in then redirect to sign_in path, however probably due to my lack of knoledge, I'm creating an infinite redirect loop. Even if I try to access sign_in without the "require_login" filter.
Here's what I've done so far:
application_controller.rb
class ApplicationController < ActionController::Base
before_filter :require_login
protect_from_forgery
#IDEA 1
#def require_login
# if current_user.role? == 'Administrator'
# redirect_to rails_admin_path
# elsif current_user.role? == (('C1' or 'D1') or ('M1' or 'M2'))
# redirect_to upload_path
# end
# end
#I saw this somewhere and It doesn't work either
def require_login
redirect_to new_user_session_path, alert: "You must be logged in to perform this action" if current_user.nil?
end
rescue_from CanCan::AccessDenied do |e|
redirect_to new_user_session_path, alert: e.message
end
end
routes.rb
Siteconfigurationlistgenerator::Application.routes.draw do
mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'
devise_for :users
# The priority is based upon order of creation:
# first created -> highest priority.
match 'upload' => 'upload_file#new'
.
.
.
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
#Define abilities for the passed in user here.
user ||= User.new #guest user (not logged in)
#a signed-in user can do everything
if user.role == 'Administrator'
#an admin can do everything
can :manage, :all
can :access, :rails_admin # grant access to rails_admin
can :dashboard # grant access to the dashboard
elsif user.role == (('C1' or 'M1') or ('D1' or 'M1'))
# can :manage, [ProductList, Inventory]
# can :read, SiteConfigurationList
# end
end
end
When I run rake routes, I get the routes for Devise and the Rails_admin routes, plus the "upload" route.
I've really tried to fix this stupid error but honestly I ran out of ideas. I'd appreciate any help you're able to provide me. Thank you in advance.
The problem is that you have a before_filter that requires a user to sign in, in your ApplicationController. Basically you are asking your users to sign in before accessing the sign in page.
You can solve this by using devise's built-in method :authenticate_user :
before_filter :authenticate_user!
Or you can specify that your before_filter doesn't run on an action from the DeviseController.
before_filter :require_login, :unless => :devise_controller?

Rails: How to install CanCan?

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

Passing params to CanCan in RoR

I have a controller with a method like;
def show
if params[:format].eql?("pdf")
// do something
elsif params[:format].eql?("csv")
// do something
end
end
But i have users with different roles. So i use CanCan to manage access control.
Now i want X role can do the action show in controller iff params[:format].eql?("csv")
I think it can be like ;can :show, resource if params[:format].eql?("csv"). So how can i send parameters to ability.rb?
Any idea?
Thanks.
In ApplicationController add the following:
# CanCan - pass params in to Ability
# https://github.com/ryanb/cancan/issues/133
def current_ability
#current_ability ||= Ability.new(current_user, params)
end
The most current answer is in the CanCan wiki: https://github.com/ryanb/cancan/wiki/Accessing-Request-Data
can takes two arguments: first is type of action that user is trying to perform on a resource, second is resource (can be class name or instance variable) itself. If you have your Ability set correctly, you should be able to do something like this:
def show
if params[:format].eql?("pdf")
// do something
elsif params[:format].eql?("csv")
if can? :read, resource
#do stuff
end
end
end
Don't forget that you have to have your user authenticated before running any CanCan checks.
can? method only returns true or false. I normally like to use authorize! method to check abilities. Unlike can, it would rise CanCan::AccessDenied error that you can rescue and process gracefully. Something in the lines of:
#models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role? :admin
can :manage, :all
elsif user.role? :hiring_manager
can [:read, :update], Post, user_id: user.id
end
end
end
#controllers/posts_controller.rb
class PostsController < ApplicationController::Base
before_filter :authenticate_user
def show
#post = Post.find(params[:id])
authorize! :read, #post # will thorow an exception if not allowed
end
end
Then, I just catch the exception on ApplicationController level.

Resources