Sunfox


ActionMailer n’aime pas vos URLs (par défaut)

Faire un lien dans un email dans une application Ruby on Rails devrait être aussi simple que de faire un lien dans le reste de l’application, non ?

Photo of a miniature train, by Sunny

Problème

Mais voilà, un simple link_to root_url renvoie une exception car il faut un nom de domaine pour faire une URL. Pour ça il faut configurer un nom de domaine par défaut pour chaque environnement.

En développement un site varie beaucoup selon les configurations et les tests. Par exemple foo.dev, localhost, sunny.local et 192.168.1.42.foo.xip.io, sont des noms de domaines possibles et utiles pour mon application en développement. Le port (80, 3000, 8080) et le protocole (http ou https) sont également des paramètres qui peuvent changer lors des différents tests.

En production votre nom de domaine peut également varier. Par exemple en.cults3d.com et fr.cults3d.com sont deux noms de domaines utilisés par la même application, en même temps. Il est donc impossible de trouver un nom de domaine par défaut à utiliser sur chaque lien.

C’est pénible de devoir spécifier le domaine à chaque fois, c’est source de bugs.

Solution

Pour éviter cette configuration « en dur » il existe une manipulation simple qui consiste à modifier l’URL par défaut à chaque requête.

En ajoutant à son application_controller.rb :

before_filter :make_action_mailer_use_request_host_and_protocol
def make_action_mailer_use_request_host_and_protocol
  ActionMailer::Base.default_url_options[:protocol] = request.protocol
  ActionMailer::Base.default_url_options[:host] = request.host_with_port
end

Sécurité

Attention, cette solution ne vous prémunit pas contre le host injection exploit.

Si votre site déclenche un email (demande de mot de passe par exemple) et qu’un méchant indique comme hôte « evil.com » alors vous allez envoyer des emails ayant des liens en « evil.com ».

Si votre site utilise du cache ces attaques touchent sans doute déjà votre application ! Pour s’en prémunir, ajoutez donc un filtre des noms de domaines autorisés en production.

Thread Safety

Cette solution (corrigez-moi si je me trompe) souffre d’un problème de race condition. Non, ce n’est pas un problème de racisme envers certains noms de domaines qu’elle ne peut pas blairer.

Le problème est plutôt qu’une requête plus lente peut modifier sans le vouloir le domaine d’une autre requête. Une meilleure solution thread-safe existe sans doute. Si vous avez une piste élégante je suis preneur.

Gem ça

Et parce qu’une ligne de code testée dans une librairie dédiée vaut mieux que 2 lignes copié-collées dans votre projet, vous pouvez également ajouter ce code grâce à ma petite gemme :
action_mailer_auto_url_options.

Photo of a miniature train, by Sunny

👨🏻‍🦰 Sunny Ripert

est un développeur web vivant à ParisContactArchives

Textes et contenus sous licence Creative Commons.