Rails: Only allow admin user to create new users in Rails with Devise (No external modules)
To implement the authorization, use a method on the controller
Exactly as suggested by @diego.greyrobot
class UsersController < ApplicationController
before_filter :authorize_admin, only: :create
def create
# admins only
end
private
# This should probably be abstracted to ApplicationController
# as shown by diego.greyrobot
def authorize_admin
return unless !current_user.admin?
redirect_to root_path, alert: 'Admins only!'
end
end
To sidestep the Devise 'already logged in' problem, define a new route for creating users.
We will simply define a new route to handle the creation of users and then point the form to that location. This way, the form submission does not pass through the devise controller so you can feel free to use it anywhere you want in the normal Rails way.
# routes.rb
Rails.application.routes.draw do
devise_for :users
resources :users, except: :create
# Name it however you want
post 'create_user' => 'users#create', as: :create_user
end
# users/new.html.erb
# notice the url argument
<%= form_for User.new, url: create_user_path do |f| %>
# The form content
<% end %>
This seems like the simplist approach. It just requires sub-classing the devise controller. See the docs for how to do that.
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_action :authenticate_user!, :redirect_unless_admin, only: [:new, :create]
skip_before_action :require_no_authentication
private
def redirect_unless_admin
unless current_user.try(:admin?)
flash[:error] = "Only admins can do that"
redirect_to root_path
end
end
def sign_up(resource_name, resource)
true
end
end
# config/routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { :registrations => 'registrations'}
end
Explaination:
- Subclass the registration controller and create the route for it.
- The
before_action
ensures that a user is logged in, and redirects them unless they are admin, if they try to sign up. - The already logged in issue is caused by Devise's
require_no_authentication
method and skipping it resolves the issue. - Next is that the newly created user is automatically signed in. The
sign_up
helper method which does this is overridden to do prevent automatic signup. - Finally, the
Welcome! You have signed up successfully.
sign up flash message can be changed via editing theconfig/locales/devise.en.yml
file, if desired.
The problem is conceptual. Devise is only an Authentication library not an Authorization library. You have to implement this separately or use CanCan. Fret not however, it is easy in your case to implement this since you only have one role.
Guard your user create/update/destroy action with a before filter:
class UsersController < ApplicationController
before_filter :authorize_admin, except [:index, :show]
def create
# user create code (can't get here if not admin)
end
end
class ApplicationController < ActionController::Base
def authorize_admin
redirect_to root_path, alert: 'Access Denied' unless current_user.admin?
end
end
With this simple approach you run a before filter on any controller action that can affect a user record by first checking if the user is an admin and kicking them out to the home page if they're not.