Devise CanCanCan rolify Tutorial - RolifyCommunity/rolify GitHub Wiki
This Tutorial shows you how to setup a Rails >=3.1 application with a strong and flexible authentication/authorization stack using Devise, CanCanCan and rolify (3.0 and later)
First, create a bare new rails app. If you already have an existing app with Devise and CanCanCan set up and you just want to add rolify, just add rolify in your Gemfile, run bundle install and skip to step 5
# rails new rolify_tutorial- edit the
Gemfileand add Devise, CanCanCan and rolify gems:
gem 'devise'
gem 'cancancan'
gem 'rolify'-
run
bundle installto install all required gems -
Run Devise generator
# rails generate devise:install
- Create the User model from Devise
# rails generate devise User
- Create the Ability class from CanCanCan
# rails generate cancan:ability
- Create the Role class from rolify
# rails generate rolify Role User
- Run migrations
# rake db:migrate
-
Configure Devise according to your needs. Follow Devise README for details.
-
Edit the Ability model class, add these lines in the initialize method:
if user.has_role? :admin
can :manage, :all
else
can :read, :all
end- Use the resourcify method in all models you want to put a role on. For example, if we have the Forum model:
class Forum < ActiveRecord::Base
resourcify
end- Create a User using
rails console
> user = User.new
> user.email = "[email protected]"
> user.password = "test1234"
> user.save- Add a role to the new User
> user.add_role "admin"- Check if the user has admin rights
> ability = Ability.new(user)
> ability.can? :manage, :all
=> trueIf you want to use class scoped role with CanCanCan, it's a bit tricky. Currently in CanCan 1.x, you cannot mix instance and class checking, because the two are OR-ed and class checking skips the hash of conditions (see https://github.com/CanCanCommunity/cancancan/wiki/Checking-Abilities for more details). Let's take this ability class example:
if user.has_role? :admin
can :manage, :all
else
can :read, Forum
can :write, Forum if user.has_role?(:moderator, Forum)
can :write, Forum, :id => Forum.with_role(:moderator, user).pluck(:id)
endThis won't work as you expect, because the last :write clause will always return true if you ask ability.can? :write, Forum, even if your user has only a role on an instance of Forum.
But you can use some workarounds:
- don't use the class scoped role for an instance. That means, you will need class scoped only roles and instance scoped only roles separated. In that case, it's better to use different action names like:
if user.has_role? :admin
can :manage, :all
else
can :read, Forum
can :manage, Forum if user.has_role?(:manager, Forum)
can :write, Forum, :id => Forum.with_role(:moderator, user).pluck(:id)
end- don't use
can?method for class checking. Use rolify instead. so if you want to display a button or some info only if a user has a specific class role, use this:
<% if user.has_role? :moderator %>
...
<% end %>and for the instance scoped roles, you still are able to use the Ability class:
if user.has_role? :admin
can :manage, :all
else
can :read, Forum
can :write, Forum, :id => Forum.with_role(:moderator, user).pluck(:id)
endPlease note that the with_role method allows us to restrict the Forum instances the user has a role on. It's provided by rolify library using resourcify method on the Forum class.