From e0e01a54459574b19c37b07b02c0b1cb8da8dfbd Mon Sep 17 00:00:00 2001 From: f Date: Mon, 23 Dec 2024 16:36:27 -0300 Subject: [PATCH 01/11] fix: optimizar api de sitios #18074 --- app/controllers/api/v1/sites_controller.rb | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 05abc38a..384a8e4f 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -9,22 +9,30 @@ module Api # Lista de nombres de dominios a emitir certificados def index - render json: alternative_names + api_names + www_names + render json: alternative_names.concat(api_names).concat(www_names) end private + # @param query [ActiveRecord::Relation] + # @return [Array] + def hostname_of(query) + query.pluck(Arel.sql("values->>'hostname'")).compact.uniq + end + def canonicalize(name) name.end_with?('.') ? name[0..-2] : "#{name}.#{Site.domain}" end def subdomain?(name) - name.end_with? ".#{Site.domain}" + @@subdomain ||= ".#{Site.domain}" + + name.end_with? @subdomain end # Dominios alternativos def alternative_names - (DeployAlternativeDomain.all.map(&:hostname) + DeployLocalizedDomain.all.map(&:hostname)).map do |name| + hostname_of(DeployAlternativeDomain.all).concat(hostname_of(DeployLocalizedDomain)).map do |name| canonicalize name end.reject do |name| subdomain? name @@ -33,12 +41,12 @@ module Api # Obtener todos los sitios con API habilitada, es decir formulario # de contacto y/o colaboración anónima. - # - # TODO: Optimizar def api_names Site.where(contact: true) .or(Site.where(colaboracion_anonima: true)) - .select("'api.' || name as name").map(&:name).map do |name| + .pluck(:name).map do |name| + "api.#{name}" + end.map do |name| canonicalize name end.reject do |name| subdomain? name @@ -47,8 +55,8 @@ module Api # Todos los dominios con WWW habilitado def www_names - Site.where(id: DeployWww.all.pluck(:site_id)).select("'www.' || name as name").map(&:name).map do |name| - canonicalize name + Site.where(id: DeployWww.all.pluck(:site_id)).pluck(:name).map do |name| + canonicalize "www.#{name}" end end end From a833eeb7ebc083219cf22eaafcf9fb88825c8240 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 23 Dec 2024 17:08:55 -0300 Subject: [PATCH 02/11] fix: no emitir certificados para subdominios directos porque ya se hacen por wildcard --- app/controllers/api/v1/sites_controller.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 384a8e4f..f13376fa 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -6,6 +6,8 @@ module Api class SitesController < BaseController http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'], password: ENV['HTTP_BASIC_PASSWORD'] + SUBDOMAIN = ".#{Site.domain}" + PARTS = Site.domain.split('.').count # Lista de nombres de dominios a emitir certificados def index @@ -24,13 +26,17 @@ module Api name.end_with?('.') ? name[0..-2] : "#{name}.#{Site.domain}" end + # Es un subdominio directo del dominio principal + # + # @param name [String] + # @return [Bool] def subdomain?(name) - @@subdomain ||= ".#{Site.domain}" - - name.end_with? @subdomain + name.end_with?(SUBDOMAIN) && name.split('.').count == (PARTS + 1) end # Dominios alternativos + # + # @return [Array] def alternative_names hostname_of(DeployAlternativeDomain.all).concat(hostname_of(DeployLocalizedDomain)).map do |name| canonicalize name @@ -41,6 +47,8 @@ module Api # Obtener todos los sitios con API habilitada, es decir formulario # de contacto y/o colaboración anónima. + # + # @return [Array] def api_names Site.where(contact: true) .or(Site.where(colaboracion_anonima: true)) From ac1797402808aa90242188dea26cf5a8b52e3c10 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 23 Dec 2024 17:09:17 -0300 Subject: [PATCH 03/11] =?UTF-8?q?fix:=20solo=20pedir=20contrase=C3=B1a=20e?= =?UTF-8?q?n=20producci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/sites_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index f13376fa..995a6ab6 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -4,11 +4,14 @@ module Api module V1 # API para sitios class SitesController < BaseController - http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'], - password: ENV['HTTP_BASIC_PASSWORD'] SUBDOMAIN = ".#{Site.domain}" PARTS = Site.domain.split('.').count + if Rails.env.production? + http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'], + password: ENV['HTTP_BASIC_PASSWORD'] + end + # Lista de nombres de dominios a emitir certificados def index render json: alternative_names.concat(api_names).concat(www_names) From 93011f71727b23b8577f7614690a952c90e1b468 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 23 Dec 2024 16:36:04 -0300 Subject: [PATCH 04/11] fix: usar jsonb --- app/models/deploy_alternative_domain.rb | 2 +- app/models/deploy_distributed_press.rb | 5 +++- app/models/deploy_hidden_service.rb | 2 +- app/models/deploy_local.rb | 2 -- app/models/deploy_localized_domain.rb | 3 ++- app/models/deploy_rsync.rb | 4 ++- app/models/deploy_www.rb | 2 -- app/models/deploy_zip.rb | 2 -- ...30_change_deploys_values_type_to_json_b.rb | 25 +++++++++++++++++++ db/structure.sql | 7 +++--- 10 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb diff --git a/app/models/deploy_alternative_domain.rb b/app/models/deploy_alternative_domain.rb index 75b69180..293b032b 100644 --- a/app/models/deploy_alternative_domain.rb +++ b/app/models/deploy_alternative_domain.rb @@ -2,7 +2,7 @@ # Soportar dominios alternativos class DeployAlternativeDomain < Deploy - store :values, accessors: %i[hostname], coder: JSON + store_accessor :values, :hostname DEPENDENCIES = %i[deploy_local] diff --git a/app/models/deploy_distributed_press.rb b/app/models/deploy_distributed_press.rb index bbd5a9a0..93c392c8 100644 --- a/app/models/deploy_distributed_press.rb +++ b/app/models/deploy_distributed_press.rb @@ -12,7 +12,10 @@ require 'distributed_press/v1/client/site' # Al ser publicado, envía los archivos en un tarball y actualiza la # información. class DeployDistributedPress < Deploy - store :values, accessors: %i[hostname remote_site_id remote_info distributed_press_publisher_id], coder: JSON + store_accessor :values, :hostname + store_accessor :values, :remote_site_id + store_accessor :values, :remote_info + store_accessor :values, :distributed_press_publisher_id before_create :create_remote_site! before_destroy :delete_remote_site! diff --git a/app/models/deploy_hidden_service.rb b/app/models/deploy_hidden_service.rb index 25c0c217..b558edba 100644 --- a/app/models/deploy_hidden_service.rb +++ b/app/models/deploy_hidden_service.rb @@ -2,7 +2,7 @@ # Genera una versión onion class DeployHiddenService < DeployWww - store :values, accessors: %i[onion], coder: JSON + store_accessor :values, :onion before_create :create_hidden_service! diff --git a/app/models/deploy_local.rb b/app/models/deploy_local.rb index 29a31f8c..14b00a2f 100644 --- a/app/models/deploy_local.rb +++ b/app/models/deploy_local.rb @@ -3,8 +3,6 @@ # Alojamiento local, solo genera el sitio, con lo que no necesita hacer # nada más class DeployLocal < Deploy - store :values, accessors: %i[], coder: JSON - before_destroy :remove_destination! def bundle(output: false) diff --git a/app/models/deploy_localized_domain.rb b/app/models/deploy_localized_domain.rb index 59e17dcd..5a8c1689 100644 --- a/app/models/deploy_localized_domain.rb +++ b/app/models/deploy_localized_domain.rb @@ -2,7 +2,8 @@ # Soportar dominios localizados class DeployLocalizedDomain < DeployAlternativeDomain - store :values, accessors: %i[hostname locale], coder: JSON + store_accessor :values, :hostname + store_accessor :values, :locale # Generar un link simbólico del sitio principal al alternativo def deploy(**) diff --git a/app/models/deploy_rsync.rb b/app/models/deploy_rsync.rb index fcc5a65d..a85c2d02 100644 --- a/app/models/deploy_rsync.rb +++ b/app/models/deploy_rsync.rb @@ -3,7 +3,9 @@ # Sincroniza sitios a servidores remotos usando Rsync. El servidor # remoto tiene que tener rsync instalado. class DeployRsync < Deploy - store :values, accessors: %i[hostname destination host_keys], coder: JSON + store_accessor :values, :hostname + store_accessor :values, :destination + store_accessor :values, :host_keys DEPENDENCIES = %i[deploy_local deploy_zip] diff --git a/app/models/deploy_www.rb b/app/models/deploy_www.rb index bb25cc64..aafb518f 100644 --- a/app/models/deploy_www.rb +++ b/app/models/deploy_www.rb @@ -2,8 +2,6 @@ # Vincula la versión del sitio con www a la versión sin class DeployWww < Deploy - store :values, accessors: %i[], coder: JSON - DEPENDENCIES = %i[deploy_local] before_destroy :remove_destination! diff --git a/app/models/deploy_zip.rb b/app/models/deploy_zip.rb index 85005470..9f538f3e 100644 --- a/app/models/deploy_zip.rb +++ b/app/models/deploy_zip.rb @@ -6,8 +6,6 @@ require 'zip' # # TODO: Firmar con minisign class DeployZip < Deploy - store :values, accessors: %i[], coder: JSON - DEPENDENCIES = %i[deploy_local] # Una vez que el sitio está generado, tomar todos los archivos y diff --git a/db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb b/db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb new file mode 100644 index 00000000..5824a899 --- /dev/null +++ b/db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class ChangeDeploysValuesTypeToJsonB < ActiveRecord::Migration[6.1] + def up + add_column :deploys, :values_2, :jsonb, default: {} + + Deploy.find_each do |deploy| + deploy.update values_2: JSON.parse(deploy.values) + end + + remove_column :deploys, :values + rename_column :deploys, :values_2, :values + end + + def down + add_column :deploys, :values_2, :text + + Deploy.find_each do |deploy| + deploy.update values_2: deploy.values.to_json + end + + remove_column :deploys, :values + rename_column :deploys, :values_2, :values + end +end diff --git a/db/structure.sql b/db/structure.sql index 21cf04d0..962a4069 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -882,8 +882,8 @@ CREATE TABLE public.deploys ( updated_at timestamp without time zone NOT NULL, site_id integer, type character varying, - "values" text, - rol_id integer + rol_id integer, + "values" jsonb DEFAULT '{}'::jsonb ); @@ -2719,6 +2719,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240316203721'), ('20240318183846'), ('20240319124212'), -('20240319144735'); +('20240319144735'), +('20241223185830'); From da98a912dcd25bc584bd50507b63b5110622798f Mon Sep 17 00:00:00 2001 From: f Date: Mon, 23 Dec 2024 17:28:44 -0300 Subject: [PATCH 05/11] fix: rsync --- app/models/deploy_rsync.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/models/deploy_rsync.rb b/app/models/deploy_rsync.rb index a85c2d02..50aafd70 100644 --- a/app/models/deploy_rsync.rb +++ b/app/models/deploy_rsync.rb @@ -10,6 +10,8 @@ class DeployRsync < Deploy DEPENDENCIES = %i[deploy_local deploy_zip] def deploy(output: false) + raise(ArgumentError, 'destination no está configurado') if destination.blank? + ssh? && rsync(output: output) end @@ -20,13 +22,6 @@ class DeployRsync < Deploy deploy_local.size end - # Devolver el destino o lanzar un error si no está configurado - def destination - values[:destination].tap do |d| - raise(ArgumentError, 'destination no está configurado') if d.blank? - end - end - # @return [String] def url "https://#{hostname}/" @@ -45,9 +40,9 @@ class DeployRsync < Deploy ssh_available = false Net::SSH.start(host, user, verify_host_key: tofu, timeout: 5) do |ssh| - if values[:host_keys].blank? + if self.host_keys.blank? # Guardar las llaves que se encontraron en la primera conexión - values[:host_keys] = ssh.transport.host_keys.map do |host_key| + self.host_keys = ssh.transport.host_keys.map do |host_key| "#{host_key.ssh_type} #{host_key.fingerprint}" end @@ -76,7 +71,7 @@ class DeployRsync < Deploy # # @return [Symbol] def tofu - values[:host_keys].present? ? :always : :accept_new + self.host_keys.present? ? :always : :accept_new end # Devuelve el par user host From 37939a088d1192b0dae216858c92567853cc92b2 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 28 Dec 2024 16:05:59 -0300 Subject: [PATCH 06/11] Revert "fix: los subdominios para los sitios vienen de un certificado wildcard #13159" This reverts commit 9e348bfdd81a4b0acce2b660940245ed40b26267. --- app/controllers/api/v1/sites_controller.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 05abc38a..71815530 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -9,7 +9,7 @@ module Api # Lista de nombres de dominios a emitir certificados def index - render json: alternative_names + api_names + www_names + render json: sites_names + alternative_names + api_names + www_names end private @@ -22,6 +22,13 @@ module Api name.end_with? ".#{Site.domain}" end + # Nombres de los sitios + def sites_names + Site.all.order(:name).pluck(:name).map do |name| + canonicalize name + end + end + # Dominios alternativos def alternative_names (DeployAlternativeDomain.all.map(&:hostname) + DeployLocalizedDomain.all.map(&:hostname)).map do |name| From bc330f8ce2f348750585ac42905d85fda2657b0e Mon Sep 17 00:00:00 2001 From: f Date: Sat, 28 Dec 2024 16:06:19 -0300 Subject: [PATCH 07/11] fix: no ignorar sitios con el dominio en name #13159 --- app/controllers/api/v1/sites_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 71815530..65cec67d 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -26,6 +26,8 @@ module Api def sites_names Site.all.order(:name).pluck(:name).map do |name| canonicalize name + end.reject do |name| + subdomain? name end end From b6a2e30d0340c1c462d0326890e26a17a37c03fa Mon Sep 17 00:00:00 2001 From: f Date: Sat, 28 Dec 2024 16:37:42 -0300 Subject: [PATCH 08/11] refactor: filtrar la lista completa --- app/controllers/api/v1/sites_controller.rb | 29 ++++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 5fc9ac46..bebd03a8 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -14,7 +14,13 @@ module Api # Lista de nombres de dominios a emitir certificados def index - render json: sites_names.concat(alternative_names).concat(api_names).concat(www_names) + all_names = sites_names.concat(alternative_names).concat(api_names).concat(www_names).uniq.map do |name| + canonicalize name + end.reject do |name| + subdomain? name + end.uniq + + render json: all_names end private @@ -38,23 +44,18 @@ module Api end # Nombres de los sitios + # + # @param name [String] + # @return [Array] def sites_names - Site.all.order(:name).pluck(:name).map do |name| - canonicalize name - end.reject do |name| - subdomain? name - end + Site.all.order(:name).pluck(:name) end # Dominios alternativos # # @return [Array] def alternative_names - hostname_of(DeployAlternativeDomain.all).concat(hostname_of(DeployLocalizedDomain)).map do |name| - canonicalize name - end.reject do |name| - subdomain? name - end + hostname_of(DeployAlternativeDomain.all).concat(hostname_of(DeployLocalizedDomain)) end # Obtener todos los sitios con API habilitada, es decir formulario @@ -66,17 +67,13 @@ module Api .or(Site.where(colaboracion_anonima: true)) .pluck(:name).map do |name| "api.#{name}" - end.map do |name| - canonicalize name - end.reject do |name| - subdomain? name end end # Todos los dominios con WWW habilitado def www_names Site.where(id: DeployWww.all.pluck(:site_id)).pluck(:name).map do |name| - canonicalize "www.#{name}" + "www.#{name}" end end end From 529f4140ea0a3aee1b4c3a6a71c629eda593dd11 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 28 Dec 2024 16:38:03 -0300 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20ignorar=20sitios=20de=20testing?= =?UTF-8?q?=20tambi=C3=A9n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/sites_controller.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index bebd03a8..b1711d63 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -5,6 +5,7 @@ module Api # API para sitios class SitesController < BaseController SUBDOMAIN = ".#{Site.domain}" + TESTING_SUBDOMAIN = ".testing.#{Site.domain}" PARTS = Site.domain.split('.').count if Rails.env.production? @@ -18,6 +19,8 @@ module Api canonicalize name end.reject do |name| subdomain? name + end.reject do |name| + testing? name end.uniq render json: all_names @@ -43,6 +46,14 @@ module Api name.end_with?(SUBDOMAIN) && name.split('.').count == (PARTS + 1) end + # Es un dominio de prueba + # + # @param name [String] + # @return [Bool] + def testing?(name) + name.end_with?(TESTING_SUBDOMAIN) && name.split('.').count == (PARTS + 2) + end + # Nombres de los sitios # # @param name [String] From b4cd6cf09218fd6d7c0381ac2cbe1e41f58d16cc Mon Sep 17 00:00:00 2001 From: f Date: Sat, 28 Dec 2024 16:38:15 -0300 Subject: [PATCH 10/11] fix: no duplicar resultados de la consulta --- app/controllers/api/v1/sites_controller.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index b1711d63..63cc8ea0 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -62,11 +62,12 @@ module Api Site.all.order(:name).pluck(:name) end - # Dominios alternativos + # Dominios alternativos, incluyendo todas las clases derivadas de + # esta. # # @return [Array] def alternative_names - hostname_of(DeployAlternativeDomain.all).concat(hostname_of(DeployLocalizedDomain)) + hostname_of(DeployAlternativeDomain.all) end # Obtener todos los sitios con API habilitada, es decir formulario From ce21a9830769d971194fa441aeda3e7c4c53eca9 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 28 Dec 2024 18:09:39 -0300 Subject: [PATCH 11/11] fix: los certificados de la api tienen menor prioridad --- app/controllers/api/v1/sites_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 63cc8ea0..8f3cbaa2 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -15,7 +15,7 @@ module Api # Lista de nombres de dominios a emitir certificados def index - all_names = sites_names.concat(alternative_names).concat(api_names).concat(www_names).uniq.map do |name| + all_names = sites_names.concat(alternative_names).concat(www_names).concat(api_names).uniq.map do |name| canonicalize name end.reject do |name| subdomain? name