Cancancan Cant get it work with User and Admin - ruby-on-rails

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 .

Related

many to many association with cancancan in rails

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.

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_admin+cancan2 unauthorized access in dashboard

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"

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.

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