Вопрос

I am using rails 3.2.14 and devise 3.2.1, and I have an "Admin" with an STI relationship with my devise "User" model.

When I register a new Admin with the form below, I get the following error:

undefined method `new_admin_session_path' for #<ActionDispatch::Routing::RoutesProxy:0x007fa6fe068f18>

because I have:

devise_for :users, :skip => :registrations do

in my routes.

How can I register a new "Admin" and then sign them in as a "User"?

Please note that if I exchange "Admin" for "User" in my form_for, and remove the skip registrations from "User" in my routes, then I get the behaviour I want apart from it is a "User" not an "Admin".

I have created a stand alone application to demonstrate my problem. https://github.com/deathwishdave/devise_test

user.rb

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  attr_accessible :email, :password, :password_confirmation, :remember_me
end

admin.rb

class Admin < User
end

index.html.erb

<h2>Sign up</h2>

<%= form_for(Admin.new, :as => :admin, :url => registration_path(:admin)) do |f| %>
  <%= devise_error_messages! %>

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

  <div><%= f.label :password %><br />
  <%= f.password_field :password %></div>

  <div><%= f.label :password_confirmation %><br />
  <%= f.password_field :password_confirmation %></div>

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

routes.rb

  devise_for :users, :skip => :registrations do
      delete '/logout', :to => 'sessions#destroy', :as => :destroy_user_session
      get '/login', :to => 'sessions#new', :as => :new_user_session
      post '/login', :to => 'sessions#create', :as => :user_session
  end

  devise_for :admins, :skip => :sessions

  authenticated :user do
    root :to => "dashboard#index"
  end

  authenticated :admin do
    root :to => "dashboard#index"
  end

  root :to => 'welcome#index'

Update

Requirements…

  1. Four different user roles.
  2. Ability to easily add additional roles in the future.
  3. Single log-in/sign-in form for all roles.

Additional Information.

  1. All current and future roles share the same state (have the same DB fields)
  2. Every role has different relationships with other model classes.

To satisfy these requirements, I considered multiple options including STI, polymorphic associations, authorisation gems and just separate models and tables.

polymorphic associations are a poor choice because the fields between roles are the same, and it may cause difficulties with user creation. Authorisation gems such as cancan are not an option, as the roles have different relationships with other model classes. If I were to use cancan, the User model would be polluted by relationships that don’t apply to all users. Having separate models, and tables would not allow for a single point of entry for all types of user.

STI seems like a good fit because all role fields are the same, so the database will be normalised. Additional users can be added with ease.

And from a purist perspective, it makes sense. My different roles are all users, with a user being an interface or contract for all other specialties.

Это было полезно?

Решение

STI vs polymorphic

Based on the requirements and information you give about the user does STI seems like fine solution, especially if there is different logic associated to each user. However polymorphic associations would work fine as well, even if each roles doesn't have any other database informations (see http://railscasts.com/episodes/394-sti-and-polymorphic-associations).

Note on CanCan

Correct me if im wrong but CanCan seems like a good solution still, also with STI. CanCan is roled based so you can check what type of user you are authorizing and then create the rules (with relations) based on that user type/role.

Login admin as user

To answer you actual question on how to login a admin as a user should you be able to simply override the method sign_up in registration controller, like this:

class Admin::RegistrationsController < Devise::RegistrationsController
  def sign_up(resource_name, resource)
    sign_in(:user, resource)
  end
end

and add the following to your routes:

devise_for :admins, :skip => :sessions, :controllers => { :registrations => "admins/registrations" }

Also i find this link about STI and devise, it's a little different scenario than yours, but properly still some useful hints for you: http://adamrobbie.me/blog/2013-3-29-sti-with-rails-40-beta-and-devise

Другие советы

Maybe you have some really good reasons for STI, but I've always found it easier to just add another integer column to my user model that specifies a type of role. And then I deal with authorization with a tool like CanCan. This way you can add as many roles as you need by just adding a new constant to your user model.


It doesn't seem so bad to have relationships that you're not always using. You can just check the user role before allowing a user to take any actions that would make use of the relationship. Plus, these relationships shouldn't return anything meaningful if you never make use of them. I'm not sure what your concern is.

It looks like the devise is routing you to the new_<resourcetype>_session_path after account creation. You can alter this behavior by adding after_sign_up_path_for method in your RegistrationsController.

def after_sign_up_path_for(resource)
  new_user_session_path
end

It's easy to write your own registration action when you want nonstandard behavior with Devise.

For example, you can put something like this in your registration controller:

def create
  @admin = Admin.new(user_params)

  # do some special configuration for admins
  @admin.role = 'super' 

  if @admin.save
    # sign the user into the user scope (as usual) 
    # and also sign them into the admin scope (per your special case)
    sign_in(:admin, @admin)
    sign_in(:user, @admin)
    redirect_to some_path
  else
    # show error
  end
end

As long as your form parameters match what devise expects (email, password, password_confirmation, etc.) it should work fine.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top