Understanding CanCan Ability and Users - ruby-on-rails

I have Rails Admin with CanCan support in my rails app. I'm confused on one issue though. How does CanCan know what user is signed in? For example, my users can have different roles and through CanCan I assign roles for certain access into each table. When I go to localhost:3000/admin, I receive the error
CanCan::AccessDenied in RailsAdmin::MainController#dashboard
My Ability.rb file
def initialize(user)
if user and user.role.eql? :super_admin
can :manage, :all # allow superadmins to do anything
elsif user and user.role.eql? :admin
can :manage, [Location, School] # allow admins to manage only Locations and Schools
end
end
So what do I do so that user's have the ability to sign in into Rails Admin? Do I have to manually create it?

By default, CanCan will use whatever is returned by current_user. If you are using Devise within a namespace though (admin for example) then Devise actually will use current_admin_user instead. You can either create a current_user method in your ApplicationController (or some other base controller) that returns current_admin_user or overwrite the current_ability method to instantiate the Ability with current_admin_user instead.
(this is all assuming your Devise is using a namespace. By default Devise will use current_user)

You need to have a current_user method available in your controller. Assuming you have that, if you aren't signed you won't have access to a current user, so you'll need to assign a user in your ability file if it doesn't exist. In your initialize method, add user ||= User.new to make the assignment if a user doesn't already exist.
def initialize(user)
user ||= User.new
if user and user.role.eql? :super_admin
can :manage, :all # allow superadmins to do anything
elsif user and user.role.eql? :admin
can :manage, [Location, School] # allow admins to manage only Locations and Schools
end
end

Related

Guest user permissions with Rails and CanCan

Do I need to define permissions for creating user for guest? I mean can he sign up with out this permissions?
This is relative to what you need, but I would say in most cases you do not set up specific permissions for guest users. Have a look at how Ryan Bates suggest handeling it with CanCan http://railscasts.com/episodes/192-authorization-with-cancan
In effect you could capture it like this in your initialisation of your Ability model:
def initialize(user)
user ||= User.new # guest user
if user.role? :admin
#admin permissions
else
#default permissions
end
end

CanCan Abilities Definition

In my rails application, my user is able to login once and switch between accounts as they please. When they do, I need to reinitialize their abilities since the permissions are set at the account level in the database.
My initial thought is to initialize the ability class when I detect a change in the account but not sure how to accomplish this.
I am of course open to any other idea. I am new to CanCan.
The Ability class is where all user permissions are defined.
Example:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
The 'can' method is used to define permissions and requires two arguments. The first one is the action you're setting the permission for, the second one is the class of object you're setting it on.

How can i allow user with admin role to edit the role of the other users?

I use devise for authentication and only an admin can create a user.
I use cancan to assign roles to the user during user creation.
I want the admin to view all the users roles and the admin should be able to edit the roles of the users.
How can I do this?
Check out https://github.com/marklocklear/devise_multi_tentant. In ability.rb I have...
class Ability
include CanCan::Ability
def initialize(user)
can :manage, :all if user.role == "admin"
end
end
You can add a role attributes for User model.
Then, when user go to admin dashboard, you can check user's role. If user isn't admin, redirect to another page (using before_filter :function)
But, instead doing everything manually, you can use gem cancan instead, it's so popular and easy to use!
You can find the document here: GitHub
Or in rails cast episodes 192
Basically, it'll create a ability model, and you will declare privilege for user, a sample ability class
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :update, User
end
end
end
After that, you can authorize for user in action, controller or views, like the example see this link
PS: sorry if my English was too bad, thanks:)

Security: using CanCan to make sure user cannot see other users' profiles

I've got an app that uses Devise, CanCan and Rolify to deal with authentication and authorization. But I don't think I'm using these gems to the full extent. Right now the only thing in my ability class is this:
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 :read, :all
end
end
end
I found a security hole where an authenticated user is able to look at other user profiles. I fixed it by changing some code in the user controller.
def show
#user = current_user.has_role?(:admin) ? User.find(params[:id]) : current_user
end
Is this the best way to deal with this hole? Is there a best practice or a rails convention that addresses this in a different way?
From the doc:
can :read, ModelName, :user_id => user.id

How to disallow user actions in CanCan

In my ability.rb file, how can I set it so that only defined users can do things, else (an undefined/not logged in user) cannot do anything?
Example:
def initialize(user)
user ||= User.new #not logged in user
if user.admin_user?
can :manage, :all
else
#can't do anything. Cannot view, edit, or update.
end
end
Thanks!
I've never really used CanCan, but I looked over the docs, and I don't think you need to explicitly define what the user can't do.
You should just be able do something like this in your controller:
if cannot? :destroy, #project
# redirect the user or do something else to disallow access
end
cannot? should return true if the user wasn't assigned a role that has any abilities defined. Conversely can? would return false.
You could try a simple conditional set in your initialize function:
# app/models/ability.rb
def initialize(user)
if user && user.admin_user?
# Abilities for registered admin users
can :manage, :all
elsif user
# Abilities for registered users
can :read, :all
else
# Abilities for no user
end
end
When initialize is called, user will likely (depending on your authentication solution) be nil for not logged in users, which will trigger the last branch.

Resources