Comment supprimer l'itinéraire Devise pour m'inscrire?

147

J'utilise Devise dans une application Rails 3, mais dans ce cas, un utilisateur doit être créé par un utilisateur existant, qui détermine les autorisations dont il disposera.

Pour cette raison, je veux:

  • Pour supprimer l'itinéraire pour que les utilisateurs s'inscrivent .
  • Pour continuer à permettre aux utilisateurs de modifier leurs profils ( modifier leur adresse e-mail et leur mot de passe) après leur inscription

Comment puis-je faire ceci?

Actuellement, je supprime effectivement cet itinéraire en plaçant ce qui suit avant devise_for :users:

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

Cela fonctionne, mais j'imagine qu'il y a une meilleure façon, non?

Mettre à jour

Comme l'a dit Benoit Garret, la meilleure solution dans mon cas est de ne pas créer les routes d'inscription en masse et de simplement créer celles que je veux réellement.

Pour ce faire, j'ai d'abord couru rake routes, puis utilisé la sortie pour recréer celles que je voulais. Le résultat final était le suivant:

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

Notez que:

  • J'ai encore :registerabledans mon Usermodèle
  • devise/registrations gère la mise à jour de l'email et du mot de passe
  • La mise à jour d'autres attributs utilisateur - autorisations, etc. - est gérée par un autre contrôleur

Réponse réelle:

Supprimez la route pour les chemins de Devise par défaut; c'est à dire:

devise_for :users, path_names: {
  sign_up: ''
}
Nathan Long
la source
4
Je pense en fait que votre solution initiale était beaucoup plus simple et claire. Y a-t-il un réel problème de sécurité?
contre-être
Pour une raison quelconque, votre solution mise à jour a continué à générer une erreur indiquant que j'avais besoin de l'ID. Après une heure d'arrachage de cheveux et de nombreux redémarrages du serveur, il s'est en quelque sorte réparé. Je n'ai aucune idée ... mais si quelqu'un d'autre expérimente cela, continuez d'essayer!
Erik Trautman
@counterbeing - pas de problème que je sache, je n'aimais tout simplement pas avoir des itinéraires inutilisés ou dépendre de la commande.
Nathan Long le
1
"Réponse réelle" ne tue pas complètement la route si elle est redirigée depuis le contrôleur de périphérique. Le comportement par défaut vous dirigera toujours vers le chemin d'inscription si vous cliquez sur GET route comme https://example.com/users/. Voir ma réponse ci-dessous.
lacostenycoder
1
Faille de sécurité! La "réponse réelle" qui est affichée supprime uniquement le formulaire d'inscription, elle ne supprime PAS la route POST qui crée réellement l'utilisateur.
Eric Terry

Réponses:

54

J'ai essayé de le faire aussi, mais un fil de discussion sur le groupe Google de conception m'a dissuadé de rechercher une solution vraiment propre.

Je citerai José Valim (le mainteneur de Devise):

Il n'y a pas d'option simple. Vous pouvez soit fournir un correctif, soit utiliser: skip =>: enregistrable et ajouter uniquement les routes souhaitées.

La question initiale était:

Existe-t-il un bon moyen de supprimer une route spécifique (la route de suppression) de Rails?

Benoit Garret
la source
4
Tout à fait correct. En fait, j'ai proposé un patch et il a poliment décliné: "Aujourd'hui, vous pouvez ignorer tout le contrôleur. Ce n'est pas optimal en termes d'utilisation, mais configurer manuellement les routes pour l'ensemble du contrôleur est assez simple. Je pense qu'exclure les routes par nom va juste rendre le code de génération de routes plus compliqué (qu'il ne l'est déjà) car nous ne pourrons pas utiliser les helpers Rails (comme les ressources, les ressources et les amis) ». github.com/plataformatec/devise/issues/…
Nathan Long
2
Je ne sais pas si c'était le cas lorsque cette réponse a été écrite à l'origine, mais le code de la citation de José est erroné. Dans Devise 3.4.1, ce n'est :skip => :registrationspas le cas :skip => :registerable.
GMA
89

vous pouvez le faire dans votre modèle

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

changez-le en:

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

notez que le symbole a :registerableété supprimé

Voilà, rien d'autre n'est requis. Tous les itinéraires et liens vers la page d'inscription sont également supprimés par magie.

stephenmurdoch
la source
21
Malheureusement, cela supprime également l'itinéraire vers edit_user_registration, dont j'ai besoin. Comme je l'ai dit, "ils devraient toujours pouvoir modifier leurs profils".
Nathan Long
1
Ahh, OK, eh bien, je contourne normalement cela en installant le gem rails_admin , qui permet aux utilisateurs d'aller localhost:3000/adminlà où ils peuvent modifier leur compte, même avec l'objet résistant supprimé. Si ce n'est pas une solution viable, jetez un œil à CanCan qui vous permet de préciser qui peut et qui ne peut pas accéder à une ressource. J'ai tendance à ajouter des rôles tels que «admin» ou «moderator» et à verrouiller tout le monde hors des pages de connexion.
stephenmurdoch
28
Utiliser une section d'administration (qui est conçue pour permettre l'édition d'enregistrements arbitraires) pour fournir aux utilisateurs un moyen de modifier leurs propres profils est la pire idée que j'ai entendue depuis longtemps. S'il vous plaît, personne ne fait ça
Jeremy
Comment désactiver sign_inen production?
WM
30

J'ai eu un problème similaire essayé de supprimer les chemins de devise_invitable pour create et new :

avant:

 devise_for :users

routes de râteau

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

après

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

routes de râteau

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

note 1 périmètre de conception https://github.com/plataformatec/devise#configuring-routes

note 2 Je l'applique sur devise_invitable mais cela fonctionnera avec n'importe quelle fonctionnalité de périphérique * capable

Remarque importante: voyez-vous que devise_scope est sur user pas users ? c'est correct, attention à ça! Cela peut causer beaucoup de douleur en vous donnant ce problème:

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]
équivalent8
la source
Merci exactement ce que je cherchais. Pour les autres utilisateurs de cette solution, j'ai dû ajouter /: id à la définition de la route put.
John
21

J'ai trouvé un autre article similaire à celui-ci et je voulais partager une réponse donnée par @chrisnicola. Dans le message, ils tentaient de bloquer uniquement les inscriptions des utilisateurs pendant la production.

Vous pouvez également modifier le contrôleur des inscriptions. Vous pouvez utiliser quelque chose comme ceci:

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

Cela remplacera le contrôleur de l'appareil et utilisera les méthodes ci-dessus à la place. Ils ont ajouté des messages flash au cas où quelqu'un accédait d'une manière ou d'une autre à la page d'inscription. Vous devriez également pouvoir changer la redirection vers n'importe quel chemin que vous aimez.

Aussi dans "config / routes.rb", vous pouvez ajouter ceci:

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

Le laisser ainsi vous permettra d'utiliser le modèle standard pour modifier votre profil. Si vous le souhaitez, vous pouvez toujours remplacer l'option de modification du profil en incluant

  def update
  end

dans le "app / controllers / registrations_controller.rb"

Daniel
la source
13

C'est une vieille question - mais j'ai récemment résolu le même problème et proposé une solution beaucoup plus élégante 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

Et il donne les noms par défaut des routes nommées (comme cancel_user_registration) sans être excessivement verbeux.

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 sortie avec les modules de périphérique par défaut:

                  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
la source
12

Vous pouvez surcharger "devise_scope" en le plaçant avant "devise_for".

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

devise_for :users

Je ne sais pas si c'est le meilleur moyen, mais c'est ma solution actuellement, car il redirige simplement vers la page de connexion.

Minuit
la source
1
J'ai adopté une approche similaire, mais je voulais que l'URL change également, alors je suis allé avec `get" / users / sign_up ",: to => redirect (" / ")`
dinjas
Résolution si simple et la plus facile. Mais cette résolution a un problème d'une minute. L'adresse est restée. Si vous entrez, /users/sign_upvous aurez accès à l' adresse sites#indexnon sign_upmais toujours restée /users/sign_up.
Penguin
5

J'ai aimé la réponse de @ max , mais en essayant de l'utiliser, j'ai rencontré une erreur car elle devise_mappingétait nulle.

J'ai légèrement modifié sa solution pour en faire une qui semble résoudre le problème. Il fallait encapsuler l'appel à l' resourceintérieur 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

Notez que devise_scopeattend le singulier :useralors que resourceattend le pluriel :users.

Dvanoni
la source
4

Faites ceci dans 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

vous obtiendrez une erreur maintenant pendant que vous viendrez à la page de connexion, pour le corriger. Effectuez cette modification dans: 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
la source
Cela a fonctionné pour moi (je n'ai utilisé que le devise_foret le asbloc) et j'ai dû supprimer :registerablele modèle.
dusan
3

J'ai trouvé que cela fonctionnait bien sans jouer avec les routes ni ajouter des méthodes de contrôleur d'application. Mon approche consiste à remplacer la méthode de conception. Ajoutez ceci à app/controllers/devise/registrations_controller.rb j'ai omis les autres méthodes par souci de concision.

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

Aussi pour supprimer l'illusion que ce chemin est toujours accessible à partir d'autres vues, vous pouvez également supprimer ce code 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
la source
2

Pour d'autres dans mon cas.
Avec devise (3.5.2).
J'ai supprimé avec succès les itinéraires d'inscription, mais j'ai conservé ceux pour modifier le profil, avec le code suivant.

#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
la source
1

Voici l'itinéraire légèrement différent que j'ai emprunté. Cela permet de ne pas avoir à remplacer la devise/shared/_links.html.erbvue.

Dans app/models/user.rb:

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

Dans 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

Avant:

$ 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

Après:

$ 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
la source
Si vous ne voulez pas d'itinéraires redondants, ignorez tous les itinéraires par défaut, c'estdevise_for :users, skip: :all
elquimista
0

J'ai eu le même problème et j'ai trouvé une mauvaise pratique de rediriger les utilisateurs depuis la page d'inscription. Donc, ma solution est essentiellement de ne pas utiliser :registrabledu tout.

Ce que j'ai fait, c'est de créer une page similaire comme modifier les détails de l'utilisateur qui ressemblait à:

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

Donc, ce formulaire se soumet à un nouveau point de fin de publication qui met à jour le mot de passe, qui ressemble à:

  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

Plus tard, vous pouvez utiliser @resultdans votre vue pour indiquer à l'utilisateur si le mot de passe est mis à jour ou non.

Sarp Kaya
la source
0

En modifiant les itinéraires, il y a toute une série d'autres problèmes qui en découlent. La méthode la plus simple que j'ai trouvée est de faire ce qui suit.

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
la source
Fonctionne, mais voulez-vous vraiment exécuter cette méthode sur chaque action?
lacostenycoder
-7

Vous pouvez modifier la devisegemme elle-même. Tout d'abord, exécutez cette commande pour trouver l'emplacement d'installation de using:

gem which devise

Supposons que le chemin soit: /usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise

Ensuite aller à

/usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise/lib/devise/railset éditez routes.rbdans ce répertoire. Il existe une méthode appelée def devise_registration(mapping, controllers)que vous pouvez modifier pour vous débarrasser de la nouvelle action. Vous pouvez également supprimer complètement les mappages pourdevise_registration

Ankit Soni
la source
+1 pour une suggestion alternative, mais bifurquer un joyau me semble moins souhaitable que de mettre du code maladroit dans mes routes.
Nathan Long
4
en pratique générale, c'est grand Non-Non! vous devez garder les gemmes telles qu'elles sont et si vous avez besoin de changer quelque chose, il suffit de les patcher
équivalent8
Je suis d'accord avec vous dans ce cas, mais en général, je ne pense pas que vous devriez hésiter à apporter des modifications aux bibliothèques / gemmes que vous utilisez comme alternative au code de patch monkey dans un tas d'endroits différents. La possibilité d'adapter une bibliothèque à vos besoins est l'un des grands avantages de l'utilisation du code open source IMO.
Ankit Soni
Si vous allez modifier la gemme, au moins fourchez-la et pointez votre Gemfile vers votre gemme patchée de singe (sur github par exemple). Je l'ai fait à plusieurs reprises. Le processus est le suivant: forkez un gem, clonez votre fork localement, modifiez votre version locale, poussez vers votre dépôt distant et pointez Gemfile dessus. (ie gem 'devise', github: 'yourusername/devise', branch: "master")
lacostenycoder