title: Devise / Rolify / CanCan 2.0: Preventing users from changing a user's role
body: <p>I am using Devise for authentication, Rolify for role management and CanCan 2.0 for authorization.</p>
  
  <p>I am trying to allow the :admin role to change a user's roles, but disallow all other users access.</p>
  
  <p>Here is what I have tried and is not working:</p>
  
  <pre><code>#ability.rb
  class Ability
    include CanCan::Ability
  
    def initialize(user)
      if user.has_role? :admin
        can :access, :all
      elsif user.has_role? :moderator
        can [:index, :read, :update, :destroy], :users, :user_id =&gt; user.id
        cannot :access, :users, [:role_ids]
      end
  end
  
  #application_controller.rb
  ...
  rescue_from CanCan::Unauthorized do |exception|
      redirect_to root_url, :alert =&gt; exception.message
    end
  </code></pre>
  
  <p>I have intentionally left the association in my user form:</p>
  
  <pre><code>#_form.html.erb
  &lt;%= simple_form_for @user do |f| %&gt;
    &lt;%= f.association :roles, as: :check_boxes %&gt;
    &lt;%#= f.association :roles, as: :check_boxes if can? :update, @user, :roles %&gt;
    &lt;%= f.button :submit %&gt;
  &lt;% end %&gt;
  </code></pre>
  
  <p>controller</p>
  
  <pre><code>#users_controller.rb
  class UsersController &lt; ApplicationController
  
    before_filter :authenticate_user!
    load_and_authorize_resource
  
    def index
      @users = User.accessible_by(current_ability)
    end
  
    def new
      @user = User.new
    end
  
    def create
      @user = User.new(params[:user])
    end
  
    def show
      @user = User.find(params[:id])
    end
  
    def edit
      @user = User.find(params[:id])
    end
  
    def update
      @user = User.find(params[:id])
  
      @user.update_without_password(params[:user])
  
      if successfully_updated
        redirect_to @user
      else
        render :action =&gt; "edit"
      end
    end
  end
  </code></pre>
  
  <p>and the model:</p>
  
  <pre><code>#user.rb
      class User &lt; ActiveRecord::Base
        rolify
  
        attr_accessible :role_ids
      ...
  </code></pre>
  
  <p>Now if a user who has the role of :moderator tries to change another user's (or his own) roles, here is what happens:</p>
  
  <ol>
  <li>A CanCan::Unauthorized exception is thrown and the user is redirected to root_url</li>
  <li>The roles are changed for the user</li>
  </ol>
  
  <p>I am confused. If the exception happens, why are the changes still made? I am probably doing something very wrong :)</p>
  
  <p>I have tried manipulating the query params depending on a users role in users_controller.rb If I put a log statement right after def update, here is my output:</p>
  
  <pre><code>2013-04-24 12:42:21 [4161] DEBUG    (0.1ms)  BEGIN
  2013-04-24 12:42:21 [4161] DEBUG    (0.3ms)  INSERT INTO "users_roles" ("user_id", "role_id") VALUES (5, 1)
  2013-04-24 12:42:21 [4161] DEBUG    (0.4ms)  COMMIT
  2013-04-24 12:42:21 [4161] DEBUG   User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", "5"]]
  2013-04-24 12:42:21 [4161] DEBUG {"username"=&gt;"Blabla", "email"=&gt;"bla@bla.com", "password"=&gt;"", "password_confirmation"=&gt;"", "approved"=&gt;"1", "role_ids"=&gt;["1", "2", ""]}
  </code></pre>
  
  <p>I must be overlooking something...</p>