1

I'm new to rails (using rails 6), and I have a question that I didn't see answered on the site for my specific use case.

The basic scenario is the same as in many question - adding role based authentication to a rails application when using devise.

The way I implemented this was to create a roles table, and having a one to many relations between it and the users table.

I need only two users: guest and admin, and they have 1, 2 ids, respectively. I inserted the values into the roles table manually, using the rails console.

Now, I'm having trouble with the sign up form, which is the default one devise gem created:

<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

What I want to add is a select field that will show "admin" and "guest", and when the user submit the form, the right role_id will be added to the user record in the user table, but I don't get how to inform the devise view on the role table existence and how to match the string value of the role with the role id the I want the user to accualy have.

So my questions are:

  1. How to add the roles to a select field in the devise sign up form?

  2. How to handle after the user selects one of the roles string names, and match it with role id that will be added to the user when the form is processed?

I saw the following questions and many like them, but I didn't saw any of them dealing with matching string with id before the form is submitted:

Devise Add Admin Role

how to automatically add a role to a user when signing up using devise

Adding Fields To Devise Sign Up Using Rails 4

Multiple models associated with devise user - fill in one model based on selection of role

If there is a question answering my issue, I'm sorry and I would be glad to get the link and close this question.

If there is any more info you need, please let me know.

Thanks!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
mizenetofa1989
  • 117
  • 2
  • 16
  • Do you have a reason for using a separate roles table instead of something like an enum (or admin flag) on users to differentiate admins from guests? If there is no reason then I would suggest using an enum...it will make things a lot simpler. – Mark Merritt Oct 23 '19 at 16:05
  • @MarkMerritt I want to support the option to add more roles in the future, so I dont want to add to the user just admin: boolean. true/false, because there might be more later on. – mizenetofa1989 Oct 23 '19 at 16:43
  • [enums](https://api.rubyonrails.org/v5.2.3/classes/ActiveRecord/Enum.html) seem to be what you are looking for, unless there is some other data attached to a role that you need to store. – Mark Merritt Oct 23 '19 at 16:49
  • @MarkMerritt OK, but I dont see how to add it to devise conroller and integrate it in the form. Plus, I dont see how enum solved the mapping between the string value to the id before the form is submitted – mizenetofa1989 Oct 23 '19 at 16:52
  • @mizenetofa1989...It certainly doesn't solve the problem but I wanted to make sure I understood the problem you were facing in a comment before giving a possible answer. – Mark Merritt Oct 23 '19 at 17:25

1 Answers1

1

Using an enum to differentiate admins from guests seems like the best option and will prevent you from having to complicate your model/form with unnecessary things like nested attributes.

Here's an example...

Create a role column on the users table.

$ rails g migration add_role_to_users role:integer

In your migration file, make sure to set the default to 0.

add_column :users, :role, :integer, default: 0

Then, after migrating the db, in your User model add the enum...

class User < ApplicationRecord
  enum role: { guest: 0, admin: 1 } 
  ...
end

Adding another role is as simple as adding a key/value to the role enum.

Then, in your devise form, you can add something like...

<%= f.label :role %>
<%= f.select :role, User.roles.keys %>

You will also need to make sure that you are adding role as a permitted param...you seem to be adding it as a field in signup, so in ApplicationController...

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  private

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
  end
end

If you go with this you will also have to remove your roles table and associations.

Mark Merritt
  • 2,625
  • 2
  • 12
  • 16