Как удалить маршрут Devise, чтобы зарегистрироваться?

147

Я использую Devise в приложении Rails 3, но в этом случае пользователь должен быть создан существующим пользователем, который определяет, какие разрешения он / она будет иметь.

Из-за этого я хочу:

  • Чтобы удалить маршрут для пользователей, чтобы зарегистрироваться .
  • Чтобы разрешить пользователям редактировать свои профили (изменять адрес электронной почты и пароль) после того, как они зарегистрировались

Как я могу это сделать?

В настоящее время я эффективно удаляю этот маршрут, разместив следующее devise_for :users:

match 'users/sign_up' => redirect('/404.html')

Это работает, но я думаю, что есть лучший способ, верно?

Обновить

Как сказал Бенуа Гаррет, лучшее решение в моем случае - это пропустить создание массовых маршрутов регистрации и просто создать те, которые мне действительно нужны.

Для этого я сначала запустил rake routes, а затем использовал выходные данные, чтобы воссоздать те, которые я хотел. Конечный результат был такой:

devise_for :users, :skip => [:registrations] 
as :user do
  get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
  put 'users' => 'devise/registrations#update', :as => 'user_registration'
end

Обратите внимание, что:

  • Я до сих пор :registerableв моей Userмодели
  • devise/registrations обрабатывает обновление электронной почты и пароля
  • Обновление других пользовательских атрибутов - разрешений и т. Д. - выполняется другим контроллером.

Актуальный ответ:

Удалите маршрут для путей Devise по умолчанию; то есть:

devise_for :users, path_names: {
  sign_up: ''
}
Натан Лонг
источник
4
Я действительно думаю, что ваше оригинальное решение было гораздо более простым и понятным. Есть ли реальная проблема с этим в плане безопасности?
контраргумент
По какой-то причине ваше обновленное решение продолжало выдавать ошибку, в которой говорилось, что мне нужен идентификатор. После часа стягивания волос и многих перезапусков сервера он как-то исправился. Я понятия не имею ... но если кто-то еще испытывает это, продолжайте пытаться!
Эрик Траутман
@ counterbeing - нет проблем, о которых я знаю, мне просто не нравилось иметь неиспользованные маршруты или полагаться на заказ.
Натан Лонг
1
«Фактический ответ» не завершает уничтожение маршрута, если он перенаправлен из контроллера devise. Поведение по умолчанию все равно будет направлять вас к пути регистрации, если вы нажмете GET route как https://example.com/users/. Смотрите мой ответ ниже.
Лакостеникодер
1
Ошибка безопасности! «Фактический ответ», который показан только для избавления от формы регистрации, он НЕ избавляется от маршрута POST, который фактически создает пользователя.
Эрик Терри

Ответы:

54

Я тоже пытался это сделать, но кто- то из группы разработчиков Google отговорил меня от поиска действительно чистого решения.

Я процитирую Хосе Валима (сопровождающего Devise):

Там нет прямой вариант. Вы можете предоставить исправление или использовать: skip =>: registerable и добавлять только те маршруты, которые вам нужны.

Первоначальный вопрос был:

Есть ли хороший способ удалить определенный маршрут (удалить маршрут) из Rails?

Бенуа Гаррет
источник
4
Совершенно верно. На самом деле, я предложил патч, и он вежливо отказался: «Сегодня вы можете пропустить весь контроллер. Он не оптимален с точки зрения использования, но настройка маршрутов для всего контроллера вручную довольно проста. Я считаю, что исключая маршруты по имени просто сделает код генерации маршрутов более сложным (чем он уже есть), потому что мы не сможем использовать помощников Rails (таких как ресурсы, ресурсы и друзья) ". github.com/plataformatec/devise/issues/…
Натан Лонг
2
Я не знаю, был ли это тот случай, когда этот ответ был изначально написан, но код в цитате из Хосе неверен. В Devise 3.4.1 это :skip => :registrationsне так :skip => :registerable.
GMA
89

Вы можете сделать это в вашей модели

# typical devise setup in User.rb
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

измените это на:

devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable

обратите внимание, что символ :registerable был удален

Вот и все, больше ничего не требуется. Все маршруты и ссылки на страницу регистрации тоже волшебным образом удалены.

stephenmurdoch
источник
21
К сожалению, это также удаляет маршрут edit_user_registration, который мне нужен. Как я уже сказал, «они все еще должны иметь возможность редактировать свои профили».
Натан Лонг
1
Ааа, хорошо, я обычно обхожу это путем установки гема rails_admin , который позволяет пользователям переходить туда, localhost:3000/adminгде они могут редактировать свою учетную запись, даже если удаленный объект удален. Если это не жизнеспособное решение, взгляните на CanCan, который позволяет определить, кто может и не может получить доступ к ресурсу. Я склонен добавлять роли типа «администратор» или «модератор» и блокировать всех остальных на страницах регистрации.
stephenmurdoch
28
Использование раздела администратора (который позволяет редактировать произвольные записи) для предоставления пользователям возможности редактировать свои профили - худшая идея, которую я слышал за долгое время. Пожалуйста, никто не делает это
Джереми
Как отключить sign_inв производстве?
WM
30

У меня была похожая проблема, пытался удалить devise_invitable пути для создания и новых :

перед:

 devise_for :users

грабли маршруты

accept_user_invitation GET    /users/invitation/accept(.:format)           devise/invitations#edit
       user_invitation POST   /users/invitation(.:format)                  devise/invitations#create
   new_user_invitation GET    /users/invitation/new(.:format)              devise/invitations#new
                       PUT    /users/invitation(.:format)                  devise/invitations#update

после

devise_for :users , :skip => 'invitation'
devise_scope :user do
  get "/users/invitation/accept", :to => "devise/invitations#edit",   :as => 'accept_user_invitation'
  put "/users/invitation",        :to => "devise/invitations#update", :as => nil
end

грабли маршруты

accept_user_invitation GET    /users/invitation/accept(.:format)                 devise/invitations#edit
                       PUT    /users/invitation(.:format)                        devise/invitations#update

Примечание 1 Разработать область действия https://github.com/plataformatec/devise#configuring-routes

заметка 2 Я применяю его на devise_invitable, но он будет работать с любой функцией devise *

Важное примечание: видите, что devise_scope не для пользователей, а для пользователей ? это верно, остерегайтесь этого! Это может причинить вам много боли:

Started GET "/users/invitation/accept?invitation_token=xxxxxxx" for 127.0.0.1 
Processing by Devise::InvitationsController#edit as HTML
  Parameters: {"invitation_token"=>"6Fy5CgFHtjWfjsCyr3hG"}
 [Devise] Could not find devise mapping for path "/users/invitation/accept?  invitation_token=6Fy5CgFHtjWfjsCyr3hG".
This may happen for two reasons:

1) You forgot to wrap your route inside the scope block. For example:

  devise_scope :user do
     match "/some/route" => "some_devise_controller"
  end

 2) You are testing a Devise controller bypassing the router.
   If so, you can explicitly tell Devise which mapping to use:

    @request.env["devise.mapping"] = Devise.mappings[:user]
equivalent8
источник
Спасибо именно то, что я искал. Для тех, кто использует это решение, мне пришлось добавить /: id к определению маршрута put.
Джон
21

Я нашел другой пост похожий на этот, и хотел бы поделиться ответом, который дал @chrisnicola. В посте они пытались заблокировать только регистрацию пользователей во время производства.

Вы также можете изменить контроллер регистрации. Вы можете использовать что-то вроде этого:

В "app / controllers / registrations_controller.rb"

class RegistrationsController < Devise::RegistrationsController
  def new
    flash[:info] = 'Registrations are not open.'
    redirect_to root_path
  end

  def create
    flash[:info] = 'Registrations are not open.'
    redirect_to root_path
  end
end

Это переопределит контроллер devise и будет использовать вышеописанные методы. Они добавили флеш-сообщения, если кто-то каким-то образом попал на страницу sign_up. Вы также должны иметь возможность изменить перенаправление на любой путь, который вам нравится.

Также в «config / rout.rb» вы можете добавить это:

devise_for :users, :controllers => { :registrations => "registrations" }

Оставив это так, вы сможете использовать стандартное устройство для редактирования своего профиля. Если вы хотите, вы все равно можете изменить параметр редактирования профиля, включив

  def update
  end

в "app / controllers / registrations_controller.rb"

Даниил
источник
13

Это старый вопрос, но недавно я решил ту же проблему и придумал решение, которое намного элегантнее, чем:

devise_for :users, :skip => [:registrations] 
as :user do
  get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
  put 'users' => 'devise/registrations#update', :as => 'user_registration'
end

И это дает имена по умолчанию для именованных маршрутов (например cancel_user_registration), не будучи слишком многословным.

devise_for :users, skip: [:registrations]

# Recreates the Devise registrations routes
# They act on a singular user (the signed in user)
# Add the actions you want in 'only:'
resource :users,
    only: [:edit, :update, :destroy],
    controller: 'devise/registrations',
    as: :user_registration do
  get 'cancel'
end

rake routes вывод с модулями разработки по умолчанию:

                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
       user_registration PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
Максимум
источник
12

Вы можете переопределить «devise_scope», поместив его перед «devise_for».

devise_scope :user do
  get "/users/sign_up",  :to => "sites#index"
end

devise_for :users

Не уверен, что это лучший способ, но это мое решение в настоящее время, так как он просто перенаправляет обратно на страницу входа.

полуночный
источник
1
Я применил аналогичный подход, но хотел, чтобы URL тоже менялся, поэтому пошел с `get" / users / sign_up ",: to => redirect (" / ")`
dinjas
Так просто и легко решить. Но у этого решения есть одна минутная проблема. Адрес остался. Если вы войдете , /users/sign_upто вы будете доступ к sites#indexне sign_upтолько адрес все еще остается /users/sign_up.
Пингвин
5

Мне понравился ответ @ max , но при попытке его использовать я столкнулся с ошибкой из-за того, devise_mappingчто был нулевым.

Я слегка изменил его решение, чтобы решить проблему. Требовалось завернуть звонок resourceвнутрь devise_scope.

devise_for :users, skip: [:registrations]

devise_scope :user do
  resource :users,
           only: [:edit, :update, :destroy],
           controller: 'devise/registrations',
           as: :user_registration do
    get 'cancel'
  end
end

Обратите внимание, что devise_scopeожидает единственного числа, :userтогда как resourceожидает множественного числа :users.

dvanoni
источник
4

Сделайте это вways.rb

devise_for :users, :controllers => {:registrations => "registrations"}, :skip => [:registrations]
  as :user do
    get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
    put 'users' => 'devise/registrations#update', :as => 'user_registration'
end

  devise_scope :user do
    get "/sign_in",  :to => "devise/sessions#new"
    get "/sign_up",  :to => "devise/registrations#new"
  end

Вы получите ошибку сейчас, когда заходите на страницу входа, чтобы исправить ее. Сделайте это изменение в: app / views / devise / shared / _links.erb

<% if  request.path != "/sign_in" %>
    <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
        <%= link_to "Sign up", new_registration_path(resource_name) %><br />
    <% end -%>
<% end %>
Сайед
источник
Это сработало для меня (я только использовал devise_forи asблок), и мне пришлось удалить :registerableв модели.
Душан
3

Я обнаружил, что это работает хорошо, не путаясь с маршрутами и не добавляя методы контроллера приложений. Мой подход заключается в переопределении метода разработки. Добавьте это к тому, app/controllers/devise/registrations_controller.rb что я опускаю другие методы для краткости.

class Devise::RegistrationsController < DeviseController
  ...
  # GET /resource/sign_up
  def new
    redirect_to root_path
  end
  ....
end

Также, чтобы убрать иллюзию, что этот путь все еще доступен из других представлений, вы также можете удалить этот код из app/views/devise/shared/_links.erb

<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
  <%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end -%>
lacostenycoder
источник
2

Для других в моем случае.
С devise (3.5.2).
Я успешно удалил маршруты для регистрации, но сохранил те, которые редактировали профиль, со следующим кодом.

#routes.rb
devise_for :users, skip: [:registrations]
as :user do
  get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
  put '/users(.:format)' => 'devise/registrations#update', as: 'user_registration'
  patch '/users(.:format)' => 'devise/registrations#update'
end
Micka
источник
1

Вот немного другой маршрут, по которому я пошел. Это делает его таким, что вам не нужно переопределять devise/shared/_links.html.erbвид.

В app/models/user.rb:

devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable

В config/routes.rb:

devise_for :users
devise_scope :user do
  put 'users' => 'devise/registrations#update', as: 'user_registration'
  get 'users/edit' => 'devise/registrations#edit', as: 'edit_user_registration'
  delete 'users' => 'devise/registrations#destroy', as: 'registration'
end

Перед:

$ rake routes | grep devise
           new_user_session GET    /users/sign_in(.:format)                    devise/sessions#new
               user_session POST   /users/sign_in(.:format)                    devise/sessions#create
       destroy_user_session DELETE /users/sign_out(.:format)                   devise/sessions#destroy
              user_password POST   /users/password(.:format)                   devise/passwords#create
          new_user_password GET    /users/password/new(.:format)               devise/passwords#new
         edit_user_password GET    /users/password/edit(.:format)              devise/passwords#edit
                            PATCH  /users/password(.:format)                   devise/passwords#update
                            PUT    /users/password(.:format)                   devise/passwords#update
   cancel_user_registration GET    /users/cancel(.:format)                     devise/registrations#cancel
          user_registration POST   /users(.:format)                            devise/registrations#create
      new_user_registration GET    /users/sign_up(.:format)                    devise/registrations#new
     edit_user_registration GET    /users/edit(.:format)                       devise/registrations#edit
                            PATCH  /users(.:format)                            devise/registrations#update
                            PUT    /users(.:format)                            devise/registrations#update
                            DELETE /users(.:format)                            devise/registrations#destroy

После:

$ rake routes | grep devise
           new_user_session GET    /users/sign_in(.:format)                    devise/sessions#new
               user_session POST   /users/sign_in(.:format)                    devise/sessions#create
       destroy_user_session DELETE /users/sign_out(.:format)                   devise/sessions#destroy
              user_password POST   /users/password(.:format)                   devise/passwords#create
          new_user_password GET    /users/password/new(.:format)               devise/passwords#new
         edit_user_password GET    /users/password/edit(.:format)              devise/passwords#edit
                            PATCH  /users/password(.:format)                   devise/passwords#update
                            PUT    /users/password(.:format)                   devise/passwords#update
          user_registration PUT    /users(.:format)                            devise/registrations#update
     edit_user_registration GET    /users/edit(.:format)                       devise/registrations#edit
               registration DELETE /users(.:format)                            devise/registrations#destroy
bmaddy
источник
Если вы не хотите иметь избыточные маршруты, пропустите все маршруты по умолчанию, то естьdevise_for :users, skip: :all
elquimista
0

У меня была та же проблема, и я нашел немного плохой практикой перенаправлять пользователей со страницы регистрации. Таким образом, мое решение в основном не использует :registrableвообще.

То, что я сделал, было создать похожую страницу, например, редактировать пользовательские данные, которые выглядели так:

<%= form_tag(update_user_update_path, method: :post) do %>  
    <br>
    <%= label_tag(:currPassword, 'Current password:') %> <%= password_field_tag(:currPassword) %> <br>
    <%= label_tag(:newPassword, 'New password:') %> <%= password_field_tag(:newPassword) %> <br>
    <%= label_tag(:newPasswordConfirm, 'Confirm new password:') %> <%= password_field_tag(:newPasswordConfirm) %> <br>
    <%= submit_tag('Update') %>
<% end %>

Таким образом, эта форма отправляет в новую конечную точку сообщения, которая обновляет пароль, который выглядит следующим образом:

  def update
    currPass = params['currPassword']
    newPass1 = params['newPassword']
    newPass2 = params['newPasswordConfirm']
    currentUserParams = Hash.new()
    currentUserParams[:current_password] = currPass
    currentUserParams[:password] = newPass1
    currentUserParams[:password_confirmation] = newPass2
    @result = current_user.update_with_password(currentUserParams)
  end

Позже вы можете использовать, по @resultвашему мнению, информацию о том, обновлен пароль или нет.

Сарп Кая
источник
0

При изменении маршрутов возникает целый ряд других проблем. Самый простой метод, который я нашел, это сделать следующее.

ApplicationController < ActionController::Base
  before_action :dont_allow_user_self_registration

  private

  def dont_allow_user_self_registration
    if ['devise/registrations','devise_invitable/registrations'].include?(params[:controller]) && ['new','create'].include?(params[:action])
      redirect_to root_path
    end
  end
end
Уэстон Гангер
источник
Работает, но вы действительно хотите использовать этот метод для каждого отдельного действия?
Лакостеникодер
-7

Вы можете изменить сам deviseкамень. Сначала запустите эту команду, чтобы найти установленное местоположение, используя:

gem which devise

Давайте предположим, что путь: /usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise

Затем перейдите к

/usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise/lib/devise/railsи редактировать routes.rbв этом каталоге. Существует метод, def devise_registration(mapping, controllers)который вы можете изменить, чтобы избавиться от нового действия. Вы также можете полностью удалить сопоставления дляdevise_registration

Анкит Сони
источник
+1 за альтернативное предложение, но разветвление драгоценного камня кажется мне менее желательным, чем добавление неуклюжего кода в мои маршруты.
Натан Лонг
4
в общем, это большой нет-нет! Вы должны держать драгоценные камни такими, какие они есть, и если вам нужно что-то изменить, просто исправьте их обезьяной
эквивалент 8
Я согласен с вами в этом случае, но в целом я не думаю, что вам следует уклоняться от внесения изменений в библиотеки / гемы, которые вы используете в качестве альтернативы патчированию кода обезьянами в куче разных мест. Возможность формировать библиотеку в соответствии с вашими потребностями - это один из больших плюсов использования IMO с открытым исходным кодом.
Анкит Сони
Если вы собираетесь модифицировать драгоценный камень, по крайней мере разветвите его и укажите свой Gemfile на свой пропатченный обезьяной драгоценный камень (например, на github). Я делал это несколько раз. Процесс такой: форк гем, локальное клонирование вашего форка, обезьяна, исправление вашей локальной версии, переход к удаленному репозиторию и указание на него Gemfile. (т.е. gem 'devise', github: 'yourusername/devise', branch: "master")
лакостеникодер