Search code examples
ruby-on-railscancan

Rails: CanCan many roles per user can not save checkbox value


I am following CanCan wiki to make my authorization. https://github.com/ryanb/cancan/wiki/Role-Based-Authorization

I did every step as the wiki says. I can not save the value fields 'role' and 'roles_mask', every time I submit the form, they are nil. Anybody could tell me how to fix it?

User schema

  create_table "users", force: :cascade do |t|
    t.string   "username",       
    t.string   "password_digest",
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
    t.string   "role"
    t.integer  "roles_mask"
  end

User controller

class UsersController < ApplicationController
  def new
    @user = OmsUser.new
  end

  def create
    @user = User.new(user_params)
    if @saved = @user.save
      flash[:success] = "Success"
    end
  end

  private

  def user_params
    params.require(:user).permit(:username, :password, :password_confirmation,:role, :roles_mask)
  end
end

_form.html.erb

<%= form_for @user do |f| %>
  <%= f.text_field :username %>
  <%= f.password_field :password %>
  <%= f.password_field :password_confirmation %>
  <% for role in User::ROLES %>
    <%= check_box_tag "user[roles][#{role}]", role, @user.roles.include?(role), {:name => "user[roles][]"}%>
    <%= label_tag "user_roles_#{role}", role.humanize %><br />
  <% end %>
  <%= hidden_field_tag "user[roles][]", "" %>
  <%= f.submit %>
<%end%>

User.rb

class User < ApplicationRecord
  ROLES = %w[admin user manager]

  def roles=(roles)
    self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
  end

  def roles
    ROLES.reject do |r|
      ((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
    end
  end
end

Solution

  • In the user_params you have not permitted roles:

    params.require(:user).permit(:username, :password, :password_confirmation, roles: [])
    

    And in your user model did you add these methods:

    # in models/user.rb
    def roles=(roles)
      self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
    end
    
    def roles
      ROLES.reject do |r|
        ((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
      end
    end
    

    These are also required to save the roles.