From 1d6cf11d52ab49abfc1f2c675886c0346ea404f4 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 15 Apr 2021 11:34:42 -0300 Subject: [PATCH] encontrar la api independientemente del dominio que la aloje --- app/lib/constraints/api_subdomain.rb | 24 +++++++++++++++ config/routes.rb | 2 +- .../api/v1/contact_controller_test.rb | 25 ++++++++-------- .../api/v1/csp_reports_controller_test.rb | 3 +- .../api/v1/invitades_controller_test.rb | 7 +++-- .../api/v1/posts_controller_test.rb | 20 ++++++------- .../api/v1/sites_controller_test.rb | 2 +- test/system/constraints_api_subdomain_test.rb | 29 +++++++++++++++++++ 8 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 app/lib/constraints/api_subdomain.rb create mode 100644 test/system/constraints_api_subdomain_test.rb diff --git a/app/lib/constraints/api_subdomain.rb b/app/lib/constraints/api_subdomain.rb new file mode 100644 index 0000000..e115730 --- /dev/null +++ b/app/lib/constraints/api_subdomain.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Constraints + # Detecta si el dominio comienzo con api. para servir la API. + # + # Hacemos esto porque Rails históricamente tiene un largo fijo de TLD + # y como alojamos dominios que pueden tener distintas terminaciones, + # no siempre detecta el subdominio como corresponde. + # + # Antes de llegar a este punto tenemos que tener un certificado + # correspondiente en el servidor web, que se expide dentro del + # servidor, por lo que sería la primera línea para detener + # api.cualquiercosa.que.no.existe.org si hiciera falta. + class ApiSubdomain + API_SUBDOMAIN = 'api.' + + def initialize; end + + # Sólo verificamos que el subdominio empiece con api. + def matches?(request) + request.hostname.start_with? API_SUBDOMAIN + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 70bd6b0..3a80728 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,7 +10,7 @@ Rails.application.routes.draw do get 'markdown', to: 'application#markdown' - constraints subdomain: 'api' do + constraints(Constraints::ApiSubdomain.new) do scope module: 'api' do namespace :v1 do resources :csp_reports, only: %i[create] diff --git a/test/controllers/api/v1/contact_controller_test.rb b/test/controllers/api/v1/contact_controller_test.rb index 7cb1f9c..f84e015 100644 --- a/test/controllers/api/v1/contact_controller_test.rb +++ b/test/controllers/api/v1/contact_controller_test.rb @@ -13,6 +13,7 @@ module Api @site.update contact: true, design: Design.find_by_gem('editorial-autogestiva-jekyll-theme') @site.config.write @site.reload + @host = { host: "api.#{Site.domain}" } end teardown do @@ -22,11 +23,11 @@ module Api test 'el sitio tiene que existir' do @site.destroy - get v1_site_contact_cookie_url(@site.hostname) + get v1_site_contact_cookie_url(@site.hostname, **@host) assert_not cookies[@site.name] - post v1_site_contact_url(site_id: @site.hostname, form: :contacto), + post v1_site_contact_url(site_id: @site.hostname, form: :contacto, **@host), params: { name: SecureRandom.hex, pronouns: SecureRandom.hex, @@ -41,8 +42,8 @@ module Api end test 'hay que enviar desde el sitio principal' do - get v1_site_contact_cookie_url(@site.hostname) - post v1_site_contact_url(site_id: @site.hostname, form: :contacto), + get v1_site_contact_cookie_url(@site.hostname, **@host) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto, **@host), params: { name: SecureRandom.hex, pronouns: SecureRandom.hex, @@ -57,8 +58,8 @@ module Api end test 'hay que dar consentimiento' do - get v1_site_contact_cookie_url(@site.hostname) - post v1_site_contact_url(site_id: @site.hostname, form: :contacto), + 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 }, @@ -77,14 +78,14 @@ module Api test 'enviar un mensaje genera correos' do ActionMailer::Base.deliveries.clear - redirect = @site.url + '?thanks' + redirect = "#{@site.url}?thanks" 10.times do create :rol, site: @site end - get v1_site_contact_cookie_url(@site.hostname) - post v1_site_contact_url(site_id: @site.hostname, form: :contacto), + 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 }, @@ -107,14 +108,14 @@ module Api @site.update name: 'example.org.' - redirect = @site.url + '?thanks' + redirect = "#{@site.url}?thanks" 10.times do create :rol, site: @site end - get v1_site_contact_cookie_url(@site.hostname) - post v1_site_contact_url(site_id: @site.hostname, form: :contacto), + 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, diff --git a/test/controllers/api/v1/csp_reports_controller_test.rb b/test/controllers/api/v1/csp_reports_controller_test.rb index 7720d42..1dd1f02 100644 --- a/test/controllers/api/v1/csp_reports_controller_test.rb +++ b/test/controllers/api/v1/csp_reports_controller_test.rb @@ -9,10 +9,11 @@ module Api skip post v1_csp_reports_url, + host: "api.#{Site.domain}", params: { 'csp-report': { 'document-uri': 'http://example.com/signup.html', - 'referrer': '', + referrer: '', 'blocked-uri': 'http://example.com/css/style.css', 'violated-directive': 'style-src cdn.example.com', 'original-policy': "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports" diff --git a/test/controllers/api/v1/invitades_controller_test.rb b/test/controllers/api/v1/invitades_controller_test.rb index 852b4a3..bf1ec3f 100644 --- a/test/controllers/api/v1/invitades_controller_test.rb +++ b/test/controllers/api/v1/invitades_controller_test.rb @@ -11,6 +11,7 @@ module Api @usuarie = @rol.usuarie @site.update_attribute :colaboracion_anonima, true + @host = { host: "api.#{Site.domain}" } end teardown do @@ -18,7 +19,7 @@ module Api end test 'primero hay que pedir una cookie' do - get v1_site_invitades_cookie_url(@site.hostname) + get v1_site_invitades_cookie_url(@site.hostname, **@host) assert cookies[@site.name] assert cookies['_sutty_session'] @@ -27,7 +28,7 @@ module Api test 'solo si el sitio existe' do site = SecureRandom.hex - get v1_site_invitades_cookie_url(site_id: site) + get v1_site_invitades_cookie_url(site_id: site, **@host) assert_not cookies[site] assert_not cookies['_sutty_session'] @@ -36,7 +37,7 @@ module Api test 'solo si el sitio tiene colaboracion anonima' do @site.update_attribute :colaboracion_anonima, false - get v1_site_invitades_cookie_url(@site.hostname) + get v1_site_invitades_cookie_url(@site.hostname, **@host) assert_not cookies[@site.name] assert_not cookies['_sutty_session'] diff --git a/test/controllers/api/v1/posts_controller_test.rb b/test/controllers/api/v1/posts_controller_test.rb index f683fa5..4d0e702 100644 --- a/test/controllers/api/v1/posts_controller_test.rb +++ b/test/controllers/api/v1/posts_controller_test.rb @@ -18,7 +18,7 @@ module Api end test 'no se pueden enviar sin cookie' do - post v1_site_posts_url(@site.hostname, layout: :post), params: { + post v1_site_posts_url(@site.hostname, layout: :post, **@host), params: { post: { title: SecureRandom.hex, description: SecureRandom.hex @@ -36,15 +36,15 @@ module Api test 'no se pueden enviar a sitios que no existen' do site = SecureRandom.hex - get v1_site_invitades_cookie_url(site_id: site) + get v1_site_invitades_cookie_url(site_id: site, **@host) assert_not cookies[site] - get v1_site_invitades_cookie_url(@site.hostname) + get v1_site_invitades_cookie_url(@site.hostname, **@host) assert cookies[@site.name] - post v1_site_posts_url(site_id: site, layout: :post), + post v1_site_posts_url(site_id: site, layout: :post, **@host), headers: { cookies: cookies }, params: { consent: true, @@ -61,9 +61,9 @@ module Api test 'antes hay que pedir una cookie' do assert_equal 2, @site.posts.size - get v1_site_invitades_cookie_url(@site.hostname) + get v1_site_invitades_cookie_url(@site.hostname, **@host) - post v1_site_posts_url(@site.hostname, layout: :post), + post v1_site_posts_url(@site.hostname, layout: :post, **@host), headers: { cookies: cookies, origin: @site.url @@ -88,9 +88,9 @@ module Api title = SecureRandom.hex order = (rand * 100).to_i - get v1_site_invitades_cookie_url(@site.hostname) + get v1_site_invitades_cookie_url(@site.hostname, **@host) - post v1_site_posts_url(@site.hostname, layout: :post), + post v1_site_posts_url(@site.hostname, layout: :post, **@host), headers: { cookies: cookies, origin: @site.url @@ -119,12 +119,12 @@ module Api test 'las cookies tienen un vencimiento interno' do assert_equal 2, @site.posts.size - get v1_site_invitades_cookie_url(@site.hostname) + get v1_site_invitades_cookie_url(@site.hostname, **@host) expired = (ENV.fetch('COOKIE_DURATION', '30').to_i + 1).minutes Timecop.freeze(Time.now + expired) do - post v1_site_posts_url(@site.hostname, layout: :post), + post v1_site_posts_url(@site.hostname, layout: :post, **@host), headers: { cookies: cookies, origin: @site.url diff --git a/test/controllers/api/v1/sites_controller_test.rb b/test/controllers/api/v1/sites_controller_test.rb index a2c54b6..5623edc 100644 --- a/test/controllers/api/v1/sites_controller_test.rb +++ b/test/controllers/api/v1/sites_controller_test.rb @@ -22,7 +22,7 @@ module Api end test 'se puede obtener un listado de todos' do - get v1_sites_url, headers: @authorization, as: :json + get v1_sites_url(host: "api.#{Site.domain}"), headers: @authorization, as: :json assert_equal Site.all.pluck(:name), JSON.parse(response.body) end end diff --git a/test/system/constraints_api_subdomain_test.rb b/test/system/constraints_api_subdomain_test.rb new file mode 100644 index 0000000..3bb43d6 --- /dev/null +++ b/test/system/constraints_api_subdomain_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class ConstraintsApiSubdomainTest < ActiveSupport::TestCase + setup do + @constraint = Constraints::ApiSubdomain.new + + MockRequest = Struct.new(:hostname, keyword_init: true) unless defined? MockRequest + end + + test 'cualquier subdominio que empiece con api. matchea' do + request = MockRequest.new hostname: 'api.' + + (rand * 10).to_i.times do + request.hostname += "#{SecureRandom.hex}." + + assert @constraint.matches?(request) + end + end + + test 'cualquier subdominio que no empiece con api. matchea' do + request = MockRequest.new hostname: 'panel.' + + (rand * 10).to_i.times do + request.hostname += "#{SecureRandom.hex}." + + assert_not @constraint.matches?(request) + end + end +end