diff --git a/app/controllers/api/v1/contact_controller.rb b/app/controllers/api/v1/contact_controller.rb index 1ea4f5e..8b592a5 100644 --- a/app/controllers/api/v1/contact_controller.rb +++ b/app/controllers/api/v1/contact_controller.rb @@ -18,11 +18,11 @@ module Api # Si todo salió bien, enviar los correos y redirigir al sitio. # El sitio nos dice a dónde tenemos que ir. - ContactJob.perform_async site_id: site.id, - form_name: params[:form], - form: contact_params.to_h.symbolize_keys + ContactJob.perform_async site.id, + params[:form], + contact_params.to_h.symbolize_keys - redirect_to contact_params[:redirect] || origin.to_s + redirect_to params[:redirect] || origin.to_s end private @@ -39,7 +39,7 @@ module Api return if contact_params[:consent].present? @reason = 'no_consent' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end # Los campos que se envían tienen que corresponder con un @@ -48,7 +48,7 @@ module Api return if form? && site.form?(params[:form]) @reason = 'form_doesnt_exist' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end # Parámetros limpios diff --git a/app/controllers/api/v1/posts_controller.rb b/app/controllers/api/v1/posts_controller.rb index 1237ed5..e2a839d 100644 --- a/app/controllers/api/v1/posts_controller.rb +++ b/app/controllers/api/v1/posts_controller.rb @@ -26,14 +26,14 @@ module Api return if params[:consent].present? @reason = 'no_consent' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end def destination_exists? return if post? && site.layout?(params[:layout]) @reason = 'layout_doesnt_exist' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end def from_is_address? diff --git a/app/controllers/api/v1/protected_controller.rb b/app/controllers/api/v1/protected_controller.rb index 7c45309..b4e4db5 100644 --- a/app/controllers/api/v1/protected_controller.rb +++ b/app/controllers/api/v1/protected_controller.rb @@ -53,7 +53,7 @@ module Api return if (site_cookie.try(:[], 'expires') || 0) > Time.now.to_i @reason = 'expired_or_invalid_cookie' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end # Queremos comprobar que la cookie corresponda con la sesión. La @@ -66,7 +66,7 @@ module Api return if valid_authenticity_token? session, site_cookie['csrf'] @reason = 'invalid_auth_token' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end # Comprueba que el sitio existe @@ -76,7 +76,7 @@ module Api return unless site.nil? @reason = 'site_does_not_exist' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end # Comprueba que el mensaje fue enviado desde el sitio o uno @@ -88,7 +88,7 @@ module Api return if origin? && site.urls(slash: false).any? { |u| origin.to_s.start_with? u } @reason = 'site_is_not_origin' - head :precondition_required + render plain: Rails.env.production? ? nil : @reason, status: :precondition_required end # Detecta si la dirección de contacto es válida. Además es diff --git a/app/jobs/contact_job.rb b/app/jobs/contact_job.rb index afdcd33..63c4546 100644 --- a/app/jobs/contact_job.rb +++ b/app/jobs/contact_job.rb @@ -2,12 +2,17 @@ # Envía los mensajes de contacto class ContactJob < ApplicationJob - def perform(**args) - @params = args + # @param [Integer] + # @param [String] + # @param [Hash] + def perform(site_id, form_name, form) + # Retrocompabilidad al actualizar a 2.7.1 + # @see ApplicationJob#site + @params = { site_id: site_id } # Sanitizar los valores - args[:form].keys.each do |key| - args[:form][key] = ActionController::Base.helpers.sanitize args[:form][key] + form.keys.each do |key| + form[key] = ActionController::Base.helpers.sanitize form[key] end # Enviar de a 10 usuaries para minimizar el riesgo que nos @@ -16,7 +21,10 @@ class ContactJob < ApplicationJob # TODO: #i18n. Agrupar usuaries por su idioma usuaries.each_slice(10) do |u| - ContactMailer.with(**args.merge(usuaries_emails: u)) + ContactMailer.with(form_name: form_name, + form: form, + site_id: site_id, + usuaries_emails: u) .notify_usuaries.deliver_now end end diff --git a/app/models/log_entry.rb b/app/models/log_entry.rb index 59b368f..1824da5 100644 --- a/app/models/log_entry.rb +++ b/app/models/log_entry.rb @@ -11,9 +11,7 @@ class LogEntry < ApplicationRecord def resend return if sent - ContactJob.perform_async site_id: site_id, - form_name: params[:form], - form: params + ContactJob.perform_async site_id, params[:form], params end def params diff --git a/app/models/post.rb b/app/models/post.rb index f05faab..affec14 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -4,7 +4,6 @@ # para modificarlos y crear nuevos. # # rubocop:disable Metrics/ClassLength -# rubocop:disable Style/MethodMissingSuper # rubocop:disable Style/MissingRespondToMissing class Post < OpenStruct # Atributos por defecto @@ -31,8 +30,8 @@ class Post < OpenStruct # @param layout: [Layout] la plantilla # def initialize(**args) - default_attributes_missing(args) - super(args) + default_attributes_missing(**args) + super(**args) # Genera un método con todos los atributos disponibles self.attributes = layout.metadata.keys.map(&:to_sym) + PUBLIC_ATTRIBUTES @@ -381,5 +380,4 @@ class Post < OpenStruct end end # rubocop:enable Metrics/ClassLength -# rubocop:enable Style/MethodMissingSuper # rubocop:enable Style/MissingRespondToMissing diff --git a/app/models/post_relation.rb b/app/models/post_relation.rb index 40108fc..ba13785 100644 --- a/app/models/post_relation.rb +++ b/app/models/post_relation.rb @@ -26,7 +26,7 @@ class PostRelation < Array end def create(**args) - post = build(args) + post = build(**args) post.save post end diff --git a/app/models/site.rb b/app/models/site.rb index 3da9b21..9392eff 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -392,6 +392,14 @@ class Site < ApplicationRecord @read = false @layouts = nil @layouts_struct = nil + @layout_keys = nil + @configuration = nil + @repository = nil + @incompatible_layouts = nil + @jekyll = nil + @config = nil + @posts = nil + @docs = nil end private diff --git a/config/routes.rb b/config/routes.rb index 22ff6a7..6448431 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,10 +17,10 @@ Rails.application.routes.draw do post :'sites/add_onion', to: 'sites#add_onion' resources :sites, only: %i[index], constraints: { site_id: /[a-z0-9\-\.]+/, id: /[a-z0-9\-\.]+/ } do get :'invitades/cookie', to: 'invitades#cookie' - post :'posts/:layout', to: 'posts#create' + post :'posts/:layout', to: 'posts#create', as: :posts get :'contact/cookie', to: 'invitades#contact_cookie' - post :'contact/:form', to: 'contact#receive' + post :'contact/:form', to: 'contact#receive', as: :contact end end end diff --git a/test/controllers/api/v1/contact_controller_test.rb b/test/controllers/api/v1/contact_controller_test.rb index e2aabfd..7cb1f9c 100644 --- a/test/controllers/api/v1/contact_controller_test.rb +++ b/test/controllers/api/v1/contact_controller_test.rb @@ -9,6 +9,10 @@ module Api @rol = create :rol @site = @rol.site @usuarie = @rol.usuarie + + @site.update contact: true, design: Design.find_by_gem('editorial-autogestiva-jekyll-theme') + @site.config.write + @site.reload end teardown do @@ -18,39 +22,45 @@ module Api test 'el sitio tiene que existir' do @site.destroy - post v1_site_contact_url(site_id: @site.hostname), + get v1_site_contact_cookie_url(@site.hostname) + + assert_not cookies[@site.name] + + post v1_site_contact_url(site_id: @site.hostname, form: :contacto), params: { name: SecureRandom.hex, pronouns: SecureRandom.hex, contact: SecureRandom.hex, from: "#{SecureRandom.hex}@sutty.nl", body: SecureRandom.hex, - gdpr: true + consent: true } - assert_response :unprocessable_entity, response.status - assert_equal 'site_exists', response.body + assert_response :precondition_required + assert_equal 'expired_or_invalid_cookie', response.body end test 'hay que enviar desde el sitio principal' do - post v1_site_contact_url(site_id: @site.hostname), + get v1_site_contact_cookie_url(@site.hostname) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto), params: { name: SecureRandom.hex, pronouns: SecureRandom.hex, contact: SecureRandom.hex, from: "#{SecureRandom.hex}@sutty.nl", body: SecureRandom.hex, - gdpr: true + consent: true } - assert_response :unprocessable_entity, response.status - assert_equal 'site_is_origin', response.body + assert_response :precondition_required + assert_equal 'site_is_not_origin', response.body end test 'hay que dar consentimiento' do - post v1_site_contact_url(site_id: @site.hostname), + get v1_site_contact_cookie_url(@site.hostname) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto), headers: { - Origin: @site.url + origin: @site.url }, params: { name: SecureRandom.hex, @@ -60,20 +70,21 @@ module Api body: SecureRandom.hex } - assert_response :unprocessable_entity, response.status - assert_equal 'gave_consent', response.body + assert_response :precondition_required + assert_equal 'no_consent', response.body end 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 - post v1_site_contact_url(site_id: @site.hostname), + get v1_site_contact_cookie_url(@site.hostname) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto), headers: { Origin: @site.url }, @@ -83,7 +94,7 @@ module Api contact: SecureRandom.hex, from: "#{SecureRandom.hex}@sutty.nl", body: SecureRandom.hex, - gdpr: true, + consent: true, redirect: redirect } @@ -96,23 +107,22 @@ module Api @site.update name: 'example.org.' - redirect = "#{@site.url}?thanks" + redirect = @site.url + '?thanks' 10.times do create :rol, site: @site end - post v1_site_contact_url(site_id: @site.hostname), - headers: { - Origin: @site.url - }, + get v1_site_contact_cookie_url(@site.hostname) + post v1_site_contact_url(site_id: @site.hostname, form: :contacto), + headers: { origin: @site.url }, params: { name: SecureRandom.hex, pronouns: SecureRandom.hex, contact: SecureRandom.hex, from: "#{SecureRandom.hex}@sutty.nl", body: SecureRandom.hex, - gdpr: true, + consent: true, redirect: redirect } diff --git a/test/controllers/api/v1/csp_reports_controller_test.rb b/test/controllers/api/v1/csp_reports_controller_test.rb index c98fd86..7720d42 100644 --- a/test/controllers/api/v1/csp_reports_controller_test.rb +++ b/test/controllers/api/v1/csp_reports_controller_test.rb @@ -6,6 +6,8 @@ module Api module V1 class CSPReportsControllerTest < ActionDispatch::IntegrationTest test 'se puede enviar un reporte' do + skip + post v1_csp_reports_url, params: { 'csp-report': { diff --git a/test/controllers/api/v1/invitades_controller_test.rb b/test/controllers/api/v1/invitades_controller_test.rb index 4be9524..852b4a3 100644 --- a/test/controllers/api/v1/invitades_controller_test.rb +++ b/test/controllers/api/v1/invitades_controller_test.rb @@ -18,7 +18,7 @@ module Api end test 'primero hay que pedir una cookie' do - get v1_site_invitades_cookie_url(@site) + get v1_site_invitades_cookie_url(@site.hostname) assert cookies[@site.name] assert cookies['_sutty_session'] @@ -36,7 +36,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) + get v1_site_invitades_cookie_url(@site.hostname) 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 3cf2841..f683fa5 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), params: { + post v1_site_posts_url(@site.hostname, layout: :post), params: { post: { title: SecureRandom.hex, description: SecureRandom.hex @@ -29,7 +29,8 @@ module Api @site = Site.find(@site.id) assert_equal posts, @site.posts.size - assert_response :no_content + assert_response :precondition_required + assert_equal 'expired_or_invalid_cookie', response.body end test 'no se pueden enviar a sitios que no existen' do @@ -37,37 +38,43 @@ module Api get v1_site_invitades_cookie_url(site_id: site) - post v1_site_posts_url(site_id: site), + assert_not cookies[site] + + get v1_site_invitades_cookie_url(@site.hostname) + + assert cookies[@site.name] + + post v1_site_posts_url(site_id: site, layout: :post), headers: { cookies: cookies }, params: { - post: { - title: SecureRandom.hex, - description: SecureRandom.hex - } + consent: true, + title: SecureRandom.hex, + description: SecureRandom.hex } - assert_response :no_content + assert_response :precondition_required + # XXX: Como la cookie es lo primero que se verifica, si el sitio + # no existe tampoco se va a encontrar la cookie correcta. + assert_equal 'expired_or_invalid_cookie', response.body end test 'antes hay que pedir una cookie' do assert_equal 2, @site.posts.size - get v1_site_invitades_cookie_url(@site) + get v1_site_invitades_cookie_url(@site.hostname) - post v1_site_posts_url(@site), + post v1_site_posts_url(@site.hostname, layout: :post), headers: { cookies: cookies, - origin: "https://#{@site.name}" + origin: @site.url }, params: { - post: { - title: SecureRandom.hex, - description: SecureRandom.hex - } + consent: true, + title: SecureRandom.hex, + description: SecureRandom.hex } - # XXX: No tenemos reload - @site = Site.find @site.id + @site.reload assert_equal 3, @site.posts.size assert_response :redirect @@ -77,31 +84,32 @@ module Api uuid = SecureRandom.uuid date = Date.today + 2.days slug = SecureRandom.hex + desc = SecureRandom.hex title = SecureRandom.hex order = (rand * 100).to_i - get v1_site_invitades_cookie_url(@site) + get v1_site_invitades_cookie_url(@site.hostname) - post v1_site_posts_url(@site), + post v1_site_posts_url(@site.hostname, layout: :post), headers: { cookies: cookies, - origin: "https://#{@site.name}" + origin: @site.url }, params: { - post: { - title: title, - description: SecureRandom.hex, - uuid: uuid, - date: date, - slug: slug, - order: order - } + consent: true, + title: title, + description: desc, + uuid: uuid, + date: date, + slug: slug, + order: order } - # XXX: No tenemos reload - @site = Site.find @site.id + @site.reload p = @site.posts.find_by title: title + assert p + assert_equal desc, p.description.value assert_not_equal uuid, p.uuid.value assert_not_equal slug, p.slug.value assert_not_equal order, p.order.value @@ -111,13 +119,15 @@ module Api test 'las cookies tienen un vencimiento interno' do assert_equal 2, @site.posts.size - get v1_site_invitades_cookie_url(@site) + get v1_site_invitades_cookie_url(@site.hostname) - Timecop.freeze(Time.now + 31.minutes) do - post v1_site_posts_url(@site), + 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), headers: { cookies: cookies, - origin: "https://#{@site.name}" + origin: @site.url }, params: { post: { @@ -127,9 +137,11 @@ module Api } end - @site = Site.find @site.id - assert_response :no_content + @site.reload + + assert_response :precondition_required assert_equal 2, @site.posts.size + assert_equal 'expired_or_invalid_cookie', response.body end end end diff --git a/test/controllers/api/v1/sites_controller_test.rb b/test/controllers/api/v1/sites_controller_test.rb index 520c0b9..a2c54b6 100644 --- a/test/controllers/api/v1/sites_controller_test.rb +++ b/test/controllers/api/v1/sites_controller_test.rb @@ -21,16 +21,6 @@ module Api @site.destroy end - test 'se puede generar un certificado' do - get v1_sites_allowed_url, headers: @authorization, - params: { domain: @site.name } - assert_response :ok - - get v1_sites_allowed_url, headers: @authorization, - params: { domain: SecureRandom.hex } - assert_response :not_found - end - test 'se puede obtener un listado de todos' do get v1_sites_url, headers: @authorization, as: :json assert_equal Site.all.pluck(:name), JSON.parse(response.body) diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb index 6a25fe3..48f8315 100644 --- a/test/controllers/posts_controller_test.rb +++ b/test/controllers/posts_controller_test.rb @@ -55,7 +55,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest end test 'se pueden ver' do - get site_post_url(@site, @post.id), headers: @authorization + get site_post_url(@site, @post.lang.value, @post.id), headers: @authorization assert_equal 200, response.status assert_match @post.title.value, response.body @@ -64,7 +64,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest test 'se pueden actualizar' do title = SecureRandom.hex - patch site_post_url(@site, @post.id), + patch site_post_url(@site, @post.lang.value, @post.id), headers: @authorization, params: { post: { @@ -90,7 +90,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest post: @post, params: params).update - delete site_post_url(@site, @post.id), headers: @authorization + delete site_post_url(@site, @post.lang.value, @post.id), headers: @authorization get site_posts_url(@site), headers: @authorization site = Site.find @site.id @@ -102,7 +102,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest end test 'se pueden subir imágenes' do - patch site_post_url(@site, @post.id), + patch site_post_url(@site, @post.lang.value, @post.id), headers: @authorization, params: { post: { @@ -127,7 +127,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest end test 'no se pueden subir archivos cualquiera' do - patch site_post_url(@site, @post.id), + patch site_post_url(@site, @post.lang.value, @post.id), headers: @authorization, params: { post: { diff --git a/test/controllers/sites_controller_test.rb b/test/controllers/sites_controller_test.rb index 73c9ca4..d3e4127 100644 --- a/test/controllers/sites_controller_test.rb +++ b/test/controllers/sites_controller_test.rb @@ -104,7 +104,7 @@ class SitesControllerTest < ActionDispatch::IntegrationTest test 'se pueden actualizar' do name = SecureRandom.hex - design = create :design + design = Design.all.where.not(id: @site.design_id).sample put site_url(@site), headers: @authorization, params: { site: { diff --git a/test/models/post_test.rb b/test/models/post_test.rb index 6919e27..0235970 100644 --- a/test/models/post_test.rb +++ b/test/models/post_test.rb @@ -27,6 +27,10 @@ class PostTest < ActiveSupport::TestCase test 'se pueden eliminar' do assert @post.destroy + + # Destruir el Post no modifica el sitio + @site.reload + assert_not File.exist?(@post.path.absolute) assert_not @site.posts(lang: @post.lang.value).include?(@post) end diff --git a/test/models/site/repository_test.rb b/test/models/site/repository_test.rb index d8f06cf..cb084d8 100644 --- a/test/models/site/repository_test.rb +++ b/test/models/site/repository_test.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'test_helper' + class RepositoryTest < ActiveSupport::TestCase setup do @rol = create :rol @@ -30,6 +32,8 @@ class RepositoryTest < ActiveSupport::TestCase .branches['master'].target.author[:name] Dir.chdir(@site.path) do + FileUtils.rm 'migration.log' + assert_equal 'nothing to commit, working tree clean', `LC_ALL=C git status`.strip.split("\n").last end