diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index a6dd81d..3a63826 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -21,7 +21,22 @@ module Api # TODO: Generar API v2 que use solo el hostname y no haya que # pasar site_id como parámetro redundante. def site_id - @site_id ||= Deploy.where(hostname: params[:site_id]).pluck(:site_id).first + @site_id ||= Deploy.site_name_from_hostname(params[:site_id]) + end + + # @return [Site] + def site + @site ||= Site.find_by_name(site_id) + end + + # Obtiene el hostname desde el Origin, con el hostname local como + # fallback. + # + # @return [String] + def origin_hostname + URI.parse(origin || origin_from_referer).host + rescue StandardError + "#{site_id}.#{Site.domain}" end # Referer diff --git a/app/controllers/api/v1/invitades_controller.rb b/app/controllers/api/v1/invitades_controller.rb index 236778c..09d2031 100644 --- a/app/controllers/api/v1/invitades_controller.rb +++ b/app/controllers/api/v1/invitades_controller.rb @@ -44,15 +44,10 @@ module Api # Genera el Origin correcto a partir de la URL del sitio. # - # En desarrollo devuelve el Origin enviado. - # - # XXX: Si el sitio tiene varias URLs, hay que devolver la más - # similar al Origin o vamos a estar generando errores de CORS. - # # @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin} # @return [String] def return_origin - Rails.env.production? ? Site.find_by(name: site_id).url : origin + site&.deploys&.find_by_hostname(origin_hostname)&.url end # La cookie no es accesible a través de JS y todo su contenido @@ -63,6 +58,8 @@ module Api # TODO: Volver configurable por sitio expires = ENV.fetch('COOKIE_DURATION', '30').to_i.minutes + # TODO: ¿Son necesarios estos headers en la descarga de una + # imagen? ¿No será mejor moverlos al envío de datos? headers['Access-Control-Allow-Origin'] = return_origin headers['Access-Control-Allow-Credentials'] = true headers['Vary'] = 'Origin' diff --git a/app/controllers/api/v1/protected_controller.rb b/app/controllers/api/v1/protected_controller.rb index 5a14bb2..b3b4dfc 100644 --- a/app/controllers/api/v1/protected_controller.rb +++ b/app/controllers/api/v1/protected_controller.rb @@ -118,11 +118,6 @@ module Api raise NotImplementedError end - # Encuentra el sitio o devuelve nulo - def site - @site ||= Site.find_by(name: site_id) - end - # Genera un registro con información básica para debug, quizás no # quede asociado a ningún sitio. # diff --git a/app/models/deploy.rb b/app/models/deploy.rb index 37790eb..e7e0844 100644 --- a/app/models/deploy.rb +++ b/app/models/deploy.rb @@ -27,6 +27,13 @@ class Deploy < ApplicationRecord # Cada deploy puede implementar su propia validación validates :hostname, hostname: true, unless: :implements_hostname_validation? + # Retrocompatibilidad: Encuentra el site_name a partir del hostname. + # + # @return [String,Nil] + def self.site_name_from_hostname(hostname) + where(hostname: hostname).includes(:site).pluck(:name).first + end + # Genera el hostname # # @return [String] diff --git a/test/controllers/api/v1/contact_controller_test.rb b/test/controllers/api/v1/contact_controller_test.rb index 95726ba..2bf12f1 100644 --- a/test/controllers/api/v1/contact_controller_test.rb +++ b/test/controllers/api/v1/contact_controller_test.rb @@ -114,8 +114,8 @@ module Api create :rol, site: @site end - get v1_site_contact_cookie_url(@site.name, **@host) - post v1_site_contact_url(site_id: @site.name, form: :contacto, **@host), + get v1_site_contact_cookie_url(@site.hostname, **@host) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto, **@host), headers: { origin: @site.url }, params: { name: SecureRandom.hex, @@ -130,6 +130,34 @@ module Api assert_equal redirect, response.headers['Location'] assert_equal 2, ActionMailer::Base.deliveries.size end + + test 'algunos navegadores no soportan Origin' do + ActionMailer::Base.deliveries.clear + + @site.update name: 'example' + + redirect = "#{@site.url}?thanks" + + 10.times do + create :rol, site: @site + end + + get v1_site_contact_cookie_url(@site.hostname, **@host) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto, **@host), + headers: { referer: @site.url }, + params: { + name: SecureRandom.hex, + pronouns: SecureRandom.hex, + contact: SecureRandom.hex, + from: "#{SecureRandom.hex}@sutty.nl", + body: SecureRandom.hex, + consent: true, + redirect: redirect + } + + assert_equal redirect, response.headers['Location'] + assert_equal 2, ActionMailer::Base.deliveries.size + end end end end