Reputation: 229
I want to achieve a role based authorisation in rails. for login I use the gem devise, which works perfect. but after I am including some code of cancancan like in this tutorial I get an error undefined method username and when I remove username I get an error undefined method email...
here is my code:
ability.rb (to manage what a user with a specific role can do)
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role?(:admin)
can :manage, :all
elsif user.role?(:mitarbeiter)
can :manage, :documents
can :manage, :entries
end
end
end
a part of my user.rb
ROLES = {0 => :admin, 1 => :mitarbeiter}
attr_reader :role :
def initialize(role_id = 0)
@role = ROLES.has_key?(role_id) ? ROLES[role_id] : ROLES[0]
end
def role?(role_name)
role == role_name
end
a part of my application_controller.rb
protect_from_forgery with: :exception
check_authorization
rescue_from CanCan::AccessDenied do |exception|
flash[:warning] = exception.message
redirect_to root_path
end
private
def current_user
User.new(session[:id])
end
helper_method :current_user
I really can't get whats wrong... I thought that I have to set @user like @role in user.rb but it doesn't helped
Upvotes: 1
Views: 272
Reputation: 229
I found a solution for that, And the role_id get stored in the database correctly too. here my solution:
my new ability.rb (not finish only for testing functionality)
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :mitarbeiter
can :manage, :documents
can :manage, :entries
else
can :read, :all
end
end
end
new user.rb
ROLES = %i[admin mitarbeiter]
def roles=(roles)
roles = [*roles].map { |r| r.to_sym }
self.role_id = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end
def roles
ROLES.reject do |r|
((role_id.to_i || 0) & 2**ROLES.index(r)).zero?
end
end
def has_role?(role)
roles.include?(role)
end
new application_controller.rb part
def configure_permitted_parameters
added_attrs = [:username, :email, :password, :password_confirmation, :remember_me, roles: [] ]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
here a I give the role to a user views/devise/registrations/new.html.erb
<% 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.to_s.humanize %><br />
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
I also removed some gem(paperclip, roo, rolify) that maybe were an issue I got this in my Gemfile:
gem 'devise', github: 'plataformatec/devise'
gem 'cancancan'
Upvotes: 0
Reputation: 2745
I suggest removing the User#initialize
method and attr_reader :role
declaration and create own role
method as follows:
def role
@role ||= ROLES.has_key?(role_id) ? ROLES[role_id] : ROLES[0]
end
def role?(role_name)
role == role_name
end
This way you achieve what you intended, but without overriding ActiveRecord's User#initialize
method.
Above code assumes, that role_id
is stored in database in role_id
column.
Upvotes: 2