An Admin Dashboard should be able to create users and other admins, and the application should send the new user a one-time confirmation link as well as initial password for the new account.
This post covers disabling admin and user sign up, but allowing currently signed in admins to create these new users.
The Devise
gem offers customizations to use a custom mailer and an ability to pull this off with some quick hacking.
Please note, this will cover only adding admin
, however the same process can be followed for user
to achieve similiar results.
Routes
On installation, Devise by default offers routing with a devise_for
block and this takes care of most of what we need, however there is some customization needed to both block routes that should not be public and ensuring our routing goes to the custom controller.
Rails.application.routes.draw do
devise_for :admins, skip: [:registrations]
authenticated :admin do
devise_for :admins, only: [:registrations], controllers: { registrations: 'admins/registrations' }
root to: "staticpages#index", as: :authenticated_user
resources :admins, only: [:index, :show]
end
end
Looking at this block, please note that everything except registrations
our adminstrative user must be logged in for.
This means that the user is able to do everything normally except the registration routes.
Almost all the defaults are still in place (sign_in, any kind of change…).
Make sure, make sure, make sure your routes are configured properly or you will run into issues with devise trying map a POST request to the default controller. The route needs to be configured to go to the custom controller!
Custom Devise Controller
Now that we have the devise routes programmed to point to our custom controllers, we have to make edits to our current controllers.
The changes look like the following to the app/controllers/admins/registrations_controller.rb
class Admins::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :authenticate_admin!
# POST /resource
def create
generated_password = Devise.friendly_token.first(8)
@new_admin = Admin.create!(email: sign_up_params[:email], password: generated_password)
AdminMailer.confirmation_instructions(@new_admin, @new_admin.confirmation_token, {password: generated_password}).deliver_now
render json: {
admin: @new_admin
}
end
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:email])
end
end
These two methods: create
and configure_signup_params
are needed to create the new admin by the current admin, and the create
method in this class calls the custom mailer to send off the newly generated password, and the confirmation token. This code is not perfect and should really use a job queue, but right now it will do what it is needed, no more and no less.
Overwriting Model
When the Admin.create
method is called the default mailer method is then called and the default email is sent out which is wrong!
We don’t want this, we want to specify that our controller will be calling the deliver_now
method using the custom mailer.
This means we have to overwrite the model’s send_on_create_confirmation_instructions
.
class Admin < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :trackable, :confirmable
def send_on_create_confirmation_instructions
return nil
end
end
Right now I have the most basic overwrite on that method to do nothing, but really this is all I need.
Custom Mailer
Ah yes, the custom mailer.
rails g mailer AdminMailer
will give us a custom mailer and get us off the ground.
Because this application only emails Admins, in app/config/initializers/devise.rb
specifiy that we will be using the admin mailer by changing the config.mailer
line to be config.mailer = 'AdminMailer'
. This will tell devise to use the AdminMailer class to send emails.
Our AdminMailer Class (app/mailers/admin_mailer.rb
) looks like the following:
class AdminMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts={})
@password = opts[:password]
super
end
end
Basically all we are doing is customizing the confirmation instructions when a new user is created. The confirmation_instructions
method states to set @password
to the password we specifiy (this password is passed in the when the controller calls the confirmation_instructions
method when the controller’s create
method is called).
The view is a very barebones .erb
file:
<p>Welcome <%= @email %>!</p>
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
<p>Your password is <%= @password %>
After setting these we are good to go, and testing it out it appears everything is in working order.
Resources
- How To: User custom mailer
- Previewing Emails
- Devise - Application/Devise Configuration
- Devise - Devise Mailer Config
- devise/app/controllers/devise_controller.rb
- Devise - how to generate a password for the users
- devise/app/controllers/devise/registrations_controller.rb
- How to Automatically generate password for users
- Class Devise::RegistrationsController Docs
- SO - Passing Variables to controller
- SO - Overwriting Model Email
- #send_devise_notification
- Registrations Controller Respond with JSON