I have a rails app with a user model, which is able to have several roles. I implemented this using a bitmask like this:
class User < ActiveRecord::Base
DEFAULT_ROLES = %w[developer entrepreneur]
ROLES = ['admin', 'entrepreneur', 'developer']
def has_role?(role)
roles.include?(role.to_s)
end
def is?(role)
has_role?(role)
end
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
In the signup page for the app, I want users to choose if they are an 'entrepreneur' or a 'developer'. However, I want to ensure that they are not able assign themselves (or anyone else) any other role, unless they are already an admin.
My first thought was to do this in the roles=
method by changin it to look like
def roles=(roles)
unless current_user.is?(:admin)
validates_inclusion_of roles, :in => DEFAULT_ROLES
end
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end
However, as I found out, you can't access current_user
from inside a model (which I guess makes sense if you think about it...)
My next attempt was to see if I could do this using Strong Parameters.
I was expecting it would look something like this (I'm using devise, overriding the RegistrationsController)
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
if (user_signed_in?) && (current_user.is?(:admin))
params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, {roles: User::ROLES})
else
params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, {roles: User::DEFAULT_ROLES})
end
end
def account_update_params
if (user_signed_in?) && (current_user.is?(:admin))
params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, :current_password, :about_me, {roles: User::ROLES})
else
params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, :current_password)
end
end
end
However, when I tried that, I got this: which makes me think I'm misunderstanding how Strong Parameters really works.
Is it possible to restrict what values a user can enter for any given field based on that users's role with Strong Parameters? If not, is there a different way to accomplish this?
I figured it out, here's how I did it. (This is the method overrides Devise's RegistrationController
, if you're not using devise, then simply replace whatever method controls what parameters are entered into a new user.)
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
parameters = params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, :about_me, roles: [])
unless (user_signed_in?) && (current_user.is?(:admin))
parameters[:roles] = parameters[:roles].reject { |h| !User::DEFAULT_ROLES.include? h }
parameters
end
end
def account_update_params
if can? :assign_roles, :all
params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, :current_password, :about_me, roles: [])
else
params.require(:user).permit(:name, :school, :email, :password, :password_confirmation, :current_password)
end
end
end
I just filtered out the parameter in parameters[:roles] to only include values that were contained in User::DEFAULT_ROLES
(shown above in the question), and then returned the parameters
object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With