How to use cancancan? - ruby-on-rails

I want to give rights to users in my rails app. I have 'admin' who can create, update and delete all posts and comments, 'user' who can create and update only his own comments, and 'guest' who can do none of these. For this I use the gems 'devise' and 'cancancan'. I understand the 'devise' gem, but I don't understand 'cancancan'.
In the class ability.rb, how can I write rights for these users (admin, user, guest)?

Cancancan lets you only define permissions for given context. This context might be a user role which is not a part of cancancan and hence roles have to be defined by yourself.
There are various ways to define user role, e.g.
as a Role model,
Rails enum,
as suggested here,
as a string attribute of User model.
It all depends of the use case. An example of how to define abilities can be found here. In your case, it would look like:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.reviewer? #Just a logged user
can :manage, Comment, { owner_id: user.id }
elsif user.admin?
can :manage, :all
end
end
end
class User < ActiveRecord::Base
enum role: [ :reviewer, :admin ]
end

You can refer following rails cast http://railscasts.com/episodes/192-authorization-with-cancan

If you are going to use Roles, consider the Canard gem (https://github.com/james2m/canard) which combines CanCanCan with RoleModel (https://github.com/martinrehfeld/role_model). This gives you a well organized way of stipulating roles and their abilities
For example, if you have a user model, you would set the roles as follows:
class User < ActiveRecord::Base
acts_as_user roles: [:supervisor, :manager, :writer]
end
Then you can create Ability files for each role, including a base User (not assigned any roles) and Guest (not logged in)
Canard::Abilities.for(:user) do
can :manage, User, id: user.id
cannot [:destroy], User
end
The above will allow a user to update their own information but will not allow them to delete themselves. They also will not have access to any other user record.

Related

Rails: cancancan gem condition is not working

In my project, I have a user class working with Devise authentication. Each user have one role associated with. For exemple, a manager is the role #1.
Role is a class and there is an association beetween role and user based on role_id. So a manager will have a role_id of 1.
I installed cancancan gem to manage permission for each user. Only a manager can create user account. So, I wrote this in the ability class:
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role_id == 1
can :manage, :user
else
cannot :manage, :user
end
end
In my user controller, I have this:
def new
#user = User.new
authorize! :manage, #user
end
The problem is when I login with my manager and try to access to new user page, I receive the message from cancancan saying that I don't have the permission to access the page.
I don't know if it's my condition or when I'm trying to access to user.role_id or...
Thanks for your help.
See cancancan gem : https://github.com/CanCanCommunity/cancancan
Edit:
It's normal if my IDE says that it cannot find current_user?
Also, if you check on github, there is an example of how to use cancancan:
As you see, there is an #article after the read permission. So I don't think I need to put current_user instead of #user
Finally:
Problem was the :user that was not correct. When it's a model, you need to write a capital letter first for the name of the model without ":". So instead of :user, it should be User
current_user relies on a Rails session. Since this is your #new action on your Users Controller, you don't have an active session, thus no current_user.

Rails_admin with devise

I have Rails_admin installed with devise and I want to restrict the /admin dashboard to only admins. For the moment my code looks like :
config.authenticate_with do
warden.authenticate! scope: :user
end
config.current_user_method(&:current_user)
As you can see users can get in to the dashboard so I want only the users with a boolean true in the admin column of the user table to get access to the dashboard.
How would you suggest I do this ?
If you dont want to use cancan you can do this:
config.authorize_with do
redirect_to main_app.root_path unless current_user.try(:admin?)
end
I use this and it works fine.
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

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.

Rails: Using CanCan to assign multiple roles to Users for each organization they belong to?

A User can belong to many Organizations. I would like User to be able to be assigned different roles/authorizations for each of the organization it belongs to.
For example, user "kevin" may belong to organization "stackoverflow" and "facebook." kevin should be able to be an admin for stackoverflow, and a regular member(read+write) for facebook.
However, the CanCan gem only seems to address user roles for a single organization. I'm still a beginner, but from what I can gather, the CanCan gem assumes user roles are tied only to the main app.
How would I be able to assign separate roles for different organizations, preferably using the CanCan gem?
You're thinking that you have to save roles as a string field in the User model. You don't have to, at all:
class User
has_many :roles
end
class Role
belongs_to :user
belongs_to :organization
attr_accessible :level
end
class Ability
def initialize(user)
can :read, Organization
can :manage, Organization do |organization|
user.roles.where(organization_id:organization.id,level:'admin').length > 0
end
can :write, Organization do |organization|
user.roles.where(organization_id:organization.id,level:'member').length > 0
end
end
end
we have something like this. the solution is to override the current_ability method. in your case, you probably have a join table for users and organization. let's call that user_organizations. In this join table, you probably also store the user's role for a particular organization, right? So let's use that table to define the current ability. In your application controller
def current_ability
# assuming you have a current_user and current_organization method
Ability.new UserOrganization.where(user_id: current_user.id, organization_id: current_organization.id).first
end
# ability.rb
class Ability
include CanCan::Ability
def initialize(user_organization)
user_organization ||= UserOrganization.new
case user_organization.role
when 'admin'
when '...'
when nil
# for users not a member of the organization
end
end
end
hope this gives you some idea

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:)

Resources