Como removo a rota do Devise para me inscrever?

147

Estou usando o Devise em um aplicativo Rails 3, mas nesse caso, um usuário deve ser criado por um usuário existente, que determina quais permissões ele terá.

Por isso, eu quero:

  • Para remover a rota para os usuários se inscreverem .
  • Para permitir que os usuários editem seus perfis (altere o endereço de e-mail e a senha) depois de se inscreverem

Como posso fazer isso?

Atualmente, estou removendo efetivamente essa rota colocando o seguinte antes devise_for :users:

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

Isso funciona, mas imagino que há uma maneira melhor, certo?

Atualizar

Como Benoit Garret disse, a melhor solução no meu caso é pular a criação das rotas de registros em massa e apenas criar as que eu realmente quero.

Para fazer isso, executei primeiro rake routese depois usei a saída para recriar as que eu queria. O resultado final foi o seguinte:

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

Observe que:

  • Eu ainda tenho :registerableno meu Usermodelo
  • devise/registrations lida com a atualização de email e senha
  • A atualização de outros atributos do usuário - permissões, etc. - é tratada por um controlador diferente

Resposta real:

Remova a rota para os caminhos padrão do Devise; ou seja:

devise_for :users, path_names: {
  sign_up: ''
}
Nathan Long
fonte
4
Na verdade, acho que sua solução original era muito mais simples e clara. Existe algum problema real com a segurança?
counterbeing
Por algum motivo, sua solução atualizada continuou lançando um erro dizendo que eu precisava do ID. Após uma hora de puxar o cabelo e muitos servidores reiniciarem, ele de alguma forma se consertou. Não faço ideia ... mas se alguém experimenta isso, continue tentando!
Erik Trautman
@ counterterbeing - não há problema que eu conheça, eu simplesmente não gostava de ter rotas não utilizadas ou depender de pedidos.
Nathan Long
1
"Resposta real" não completa elimina a rota se for redirecionada para dentro do controlador do dispositivo. O comportamento padrão ainda o encaminhará para o caminho de inscrição, se você acertar a rota GET https://example.com/users/. Veja minha resposta abaixo.
lacostenycoder
1
Falha de segurança! A "resposta real" mostrada apenas se livra do formulário de inscrição, NÃO se livra da rota POST que realmente cria o usuário.
23817 Eric Terry

Respostas:

54

Eu tentei fazer isso também, mas um tópico no grupo do Google ideal me dissuadiu de procurar uma solução realmente limpa.

Vou citar José Valim (o mantenedor do Devise):

Não há uma opção direta. Você pode fornecer um patch ou usar: skip =>: registrável e adicionar apenas as rotas que você deseja.

A pergunta original era:

Existe alguma maneira de remover uma rota específica (a rota de exclusão) do Rails?

Benoit Garret
fonte
4
Muito correto. Na verdade, propus um patch e ele recusou educadamente: "Hoje, você pode pular todo o controlador. Não é o ideal em termos de uso, mas configurar as rotas manualmente para todo o controlador é bastante direto. Acredito que excluir rotas pelo nome apenas tornará o código de geração de rotas mais complicado (do que já é), porque não poderemos usar os auxiliares do Rails (como recursos, recursos e amigos) ". github.com/plataformatec/devise/issues/…
Nathan Long
2
Não sei se esse foi o caso quando a resposta foi originalmente escrita, mas o código na citação de José está errado. No Devise 3.4.1, :skip => :registrationsnão é :skip => :registerable.
GMA
89

você pode fazer isso no seu modelo

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

altere para:

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

observe que o símbolo :registerablefoi removido

É isso, nada mais é necessário. Todas as rotas e links para a página de registro também são removidos magicamente.

stephenmurdoch
fonte
21
Infelizmente, isso também remove a rota para a edit_user_registrationqual eu preciso. Como eu disse, "eles ainda devem poder editar seus perfis".
Nathan Long
1
Ahh, ok, normalmente eu contoro isso instalando o gem rails_admin , que permite que os usuários acessemlocalhost:3000/admin onde podem editar suas contas, mesmo com o objeto resistente removido. Se essa não é uma solução viável, dê uma olhada no CanCan, que permite estipular quem pode ou não acessar um recurso. Costumo adicionar funções como 'admin' ou 'moderator' e bloquear todos os outros nas páginas de inscrição.
stephenmurdoch
28
Usar uma seção de administrador (criada para permitir a edição de registros arbitrários) para fornecer aos usuários uma maneira de editar seus próprios perfis é a pior ideia que já ouvi há muito tempo. Por favor, ninguém fazer isso
Jeremy
Como desativar sign_inna produção?
WM
30

Eu tive um problema semelhante tentei remover os caminhos devise_invitable para create e new :

antes:

 devise_for :users

rotas de ancinho

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

depois de

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

rotas de ancinho

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

note 1 criar escopo https://github.com/plataformatec/devise#configuring-routes

note 2 Estou aplicando-o no devise_invitable, mas ele funcionará com qualquer recurso ideal *

Nota importante: veja que o devise_scope está no usuário, não nos usuários ? está correto, cuidado com isso! Pode causar muita dor, causando esse problema:

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]
equivalente8
fonte
Obrigado exatamente o que eu estava procurando. Para outros que usam essa solução, tive que acrescentar /: id à definição de rota put.
John
21

Encontrei outro post semelhante a este e queria compartilhar uma resposta que @chrisnicola deu. Na postagem, eles estavam tentando bloquear apenas inscrições de usuários durante a produção.

Você também pode modificar o controlador de registros. Você pode usar algo como isto:

Em "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

Isso substituirá o controlador do dispositivo e usará os métodos acima. Eles adicionaram mensagens em flash, caso alguém de alguma forma tenha acessado a página de inscrição. Você também deve poder alterar o redirecionamento para qualquer caminho que desejar.

Também em "config / routes.rb" você pode adicionar isto:

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

Deixar assim permitirá que você use o dispositivo padrão para editar seu perfil. Se desejar, você ainda pode substituir a opção de editar perfil incluindo

  def update
  end

no "app / controllers / registrations_controller.rb"

Daniel
fonte
13

Essa é uma pergunta antiga - mas recentemente resolvi o mesmo problema e criei uma solução muito mais elegante que:

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

E fornece os nomes padrão para as rotas nomeadas (como cancel_user_registration) sem ser excessivamente detalhado.

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 saída com os módulos de criação padrão:

                  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
max
fonte
12

Você pode substituir o "devise_scope" colocando-o antes do "devise_for".

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

devise_for :users

Não tenho certeza se essa é a melhor maneira, mas é a minha solução no momento, pois ela é redirecionada de volta para a página de login.

Meia-noite
fonte
1
Eu segui uma abordagem semelhante, mas queria que o URL também mudasse, então fui com `get" / users / sign_up ",: to => redirect (" / ")` #
586 dinjas
Solução simples e fácil. Mas essa solução tem um minuto de problema. O endereço é mantido. Se você digitar /users/sign_up, terá acesso ao endereço sites#indexnão sign_upmas ainda permanece /users/sign_up.
Penguin
5

Gostei da resposta do @ max , mas ao tentar usá-lo, encontrei um erro devido adevise_mapping ser nulo.

Modifiquei sua solução levemente para uma que pareça resolver o problema. É necessário encerrar a chamada para resourcedentro 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

Observe que devise_scopeespera o singular, :userenquanto resourceespera o plural :users.

dvanoni
fonte
4

Faça isso em routes.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

agora você receberá um erro ao entrar na página de entrada para corrigi-lo. Faça essa alteração em: 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 %>
Syed
fonte
Isso funcionou para mim (eu usei apenas devise_foro asbloco e) e tive que removê-lo :registerableno modelo.
21414 Dusan
3

Descobri que isso funciona bem sem mexer nas rotas ou adicionar métodos de controlador de aplicativo. Minha abordagem é substituir o método ideal. Adicione isso a app/controllers/devise/registrations_controller.rb omiti os outros métodos por questões de brevidade.

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

Também para remover a ilusão de que esse caminho ainda é acessível a partir de outras visualizações, você também pode querer remover esse código de 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
fonte
2

Para outros no meu caso.
Com devise (3.5.2).
Eu removi com êxito as rotas para inscrição, mas mantive as para editar o perfil, com o seguinte código.

#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
fonte
1

Aqui está a rota um pouco diferente que eu segui. Faz com que você não precise substituir odevise/shared/_links.html.erb exibição.

Em app/models/user.rb:

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

Em 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

Antes:

$ 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

Depois de:

$ 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
fonte
Se você não quer ter rotas redundantes, ignorar todas as rotas padrão, ou sejadevise_for :users, skip: :all
Elquimista
0

Eu tive o mesmo problema e achei uma prática ruim redirecionar usuários da página de registro. Então, minha solução é basicamente não está usando :registrablenada.

O que fiz foi criar uma página semelhante, como editar detalhes do usuário, com a seguinte aparência:

<%= 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 %>

Portanto, este formulário é enviado para um novo ponto de extremidade da postagem que atualiza a senha, que se parece com:

  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

Posteriormente, você pode usar o @resultem sua exibição para informar ao usuário se a senha está atualizada ou não.

Sarp Kaya
fonte
0

Ao alterar as rotas, há muitos outros problemas que vêm com isso. O método mais fácil que encontrei é o seguinte.

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
Weston Ganger
fonte
Funciona, mas você realmente deseja executar esse método em cada ação?
lacostenycoder
-7

Você pode modificar a devisegema em si. Primeiro, execute este comando para encontrar o local instalado usando:

gem which devise

Vamos supor que o caminho seja: /usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise

Então vá para

/usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise/lib/devise/railse edite routes.rbnesse diretório. Existe um método chamado def devise_registration(mapping, controllers)que você pode modificar para se livrar da nova ação. Você também pode remover completamente os mapeamentos paradevise_registration

Ankit Soni
fonte
+1 para uma sugestão alternativa, mas bifurcar uma jóia parece menos desejável para mim do que colocar algum código estranho nas minhas rotas.
Nathan Long
4
na prática geral, isso é grande Não-Não! você deve manter gemas como elas são e se você precisa mudar algo apenas monkey patch-los
equivalent8
Eu concordo com você nesse caso, mas, em geral, não acho que você deva evitar alterações nas bibliotecas / gemas usadas como alternativa ao código de correção de macacos em vários lugares diferentes. A capacidade de moldar uma biblioteca para suas necessidades é uma das grandes vantagens do uso de código-fonte aberto IMO.
Ankit Soni
Se você deseja modificar a gema, pelo menos bifurque-a e aponte seu Gemfile para a gema remendada por seu macaco (no github, por exemplo). Eu fiz isso em várias ocasiões. O processo é: fork gem, clone seu fork localmente, remova a versão local do macaco, vá para o repositório remoto e aponte o Gemfile para ele. (ie gem 'devise', github: 'yourusername/devise', branch: "master")
lacostenycoder