Question

I have both Pundit and Devise setup and working correctly in my rails app. However I am unsure about how to let the user decide their role when signing up.

At the moment:

  • I have a URL param which is passed to the Devise new view.
  • In the form_for I set a hidden field called role to the value of the param.

This works.

But I am concerned that a malicious user could change this param to say "Admin" and now they are an admin.

How should I handle this? I don't want to put a restriction in the model as that will cause issues when I want to create an admin. Should I override the devise registrations controller to put a check in there?

Was it helpful?

Solution

You don't need to override Devise's RegistrationsController for what you're trying to do.

If you want admins to be able to create users that have an arbitrary role set, you could simply use your own controller. Devise still makes it easy to create a user yourself, so you'll just have to make a controller handling this. Of course, don't forget to protect it using Pundit so only admins can use this functionality.

This approach still works if you use the Confirmable module. As no confirmation e-mail will be sent on user creation, though, you'll either have to call user.confirm! after saving the model to immediately unlock the account, or manually send the confirmation e-mail using user.send_confirmation_instructions.

Edit:

This Pundit policy may or may not work for what you're trying to do. You will have to override the create action of Devise's RegistrationsController here in order to use Pundit's authorize method. For dryness' sake, you should also move the roles list elsewhere, perhaps into the model.

class UserPolicy < Struct.new(:current_user, :target_user)    
  def create?
    registration_roles.include?(target_user.role) if current_user.nil?
  end

  private

  def registration_roles
    %w(RED BLU Spectator)
  end
end

OTHER TIPS

After a fair amount of googling I have an answer. First stick some validation in your model for the roles Active Record Validations Guide: See 2.6 inclusion: validator option

After this your roles are validated to ensure they are correct, you could of course have a lookup table as well. Then you have two options:

  1. Use a conditional before_save Callback for new records. Then check if the new record has the role your protecting and if so raise an error. To catch later (in an overridden devise controller (see second option).
  2. Override the Devise registrations controller See this SO question. And put some checks in a completely overridden create action. Use the session to store the url param passed to the new action (also needs to be completely overridden). Then if the create action fails and redirects to new you still have access to the role in the session (as the param will be cleared from the URL unless you manipulate it).

So either way you need to override the registrations controller, its just a case of how much.

I suspect there is a way to do this with just Pundit. But I have yet to be able to get it to work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top