2020-03-23 20:46:19 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Api
|
|
|
|
module V1
|
|
|
|
# API para formulario de contacto
|
|
|
|
class ContactController < BaseController
|
2020-05-04 18:36:19 +00:00
|
|
|
# Permitir conexiones desde varios sitios, estamos chequeando más
|
|
|
|
# adelante.
|
|
|
|
skip_forgery_protection
|
|
|
|
|
2020-03-23 20:46:19 +00:00
|
|
|
# Aplicar algunos chequeos básicos. Deberíamos registrar de
|
|
|
|
# alguna forma los errores pero tampoco queremos que nos usen
|
|
|
|
# recursos.
|
|
|
|
#
|
2020-05-30 19:43:25 +00:00
|
|
|
# Devolvemos un error 428: Precondition Required dando la
|
|
|
|
# oportunidad a les visitantes de reintentar en el caso de falsos
|
|
|
|
# positivos. No devolvemos contenido para que el servidor web
|
|
|
|
# capture y muestra la página de error específica de cada sitio o
|
|
|
|
# la genérica de Sutty.
|
|
|
|
#
|
|
|
|
# XXX: Ordenar en orden ascendiente según uso de recursos.
|
|
|
|
before_action :cookie_is_valid?, unless: :performed?
|
|
|
|
before_action :valid_authenticity_token_in_cookie?, unless: :performed?
|
2020-03-23 20:46:19 +00:00
|
|
|
before_action :site_exists?, unless: :performed?
|
|
|
|
before_action :site_is_origin?, unless: :performed?
|
2020-05-30 19:43:25 +00:00
|
|
|
before_action :form_exists?, unless: :performed?
|
2020-03-23 20:46:19 +00:00
|
|
|
before_action :from_is_address?, unless: :performed?
|
|
|
|
before_action :gave_consent?, unless: :performed?
|
|
|
|
|
|
|
|
# Recibe un mensaje a través del formulario de contacto y lo envía
|
|
|
|
# a les usuaries del sitio.
|
|
|
|
#
|
|
|
|
# Tenemos que verificar que el sitio exista y que algunos campos
|
|
|
|
# estén llenos para detener spambots o DDOS. También nos vamos a
|
|
|
|
# estar apoyando en la limitación de peticiones en el servidor web.
|
|
|
|
def receive
|
|
|
|
# No hacer nada si no se pasaron los chequeos
|
|
|
|
return if performed?
|
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
# TODO: Verificar que los campos obligatorios hayan llegado!
|
|
|
|
|
2020-03-23 20:46:19 +00:00
|
|
|
# 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,
|
2020-05-30 19:43:25 +00:00
|
|
|
form_name: params[:form],
|
|
|
|
form: contact_params.to_h.symbolize_keys
|
2020-03-23 20:46:19 +00:00
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
redirect_to contact_params[:redirect] || origin.to_s
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
def site_cookie
|
|
|
|
@site_cookie ||= cookies.encrypted[site_id]
|
|
|
|
end
|
|
|
|
|
|
|
|
# Comprueba que no se haya reutilizado una cookie vencida
|
|
|
|
#
|
|
|
|
# XXX: Si el navegador envió una cookie vencida es porque la está
|
|
|
|
# reutilizando, probablemente de forma maliciosa? Pero también
|
|
|
|
# puede ser que haya tardado más de media hora en enviar el
|
|
|
|
# formulario.
|
|
|
|
def cookie_is_valid?
|
|
|
|
head :precondition_required unless (site_cookie.try(:[], 'expires') || 0) > Time.now.to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
# Queremos comprobar que la cookie corresponda con la sesión. La
|
|
|
|
# cookie puede haber vencido, así que es uno de los chequeos más
|
|
|
|
# simples que hacemos.
|
|
|
|
#
|
|
|
|
# TODO: Pensar una forma de redirigir al origen sin vaciar el
|
|
|
|
# formulario para que le usuarie recargue la cookie.
|
|
|
|
def valid_authenticity_token_in_cookie?
|
|
|
|
return if valid_authenticity_token? session, site_cookie['csrf']
|
|
|
|
|
|
|
|
head :precondition_required
|
|
|
|
end
|
|
|
|
|
2020-03-23 20:46:19 +00:00
|
|
|
# Comprueba que el sitio existe
|
|
|
|
#
|
|
|
|
# TODO: Responder con una zip bomb!
|
|
|
|
def site_exists?
|
2020-05-30 19:43:25 +00:00
|
|
|
head :precondition_required if site.nil?
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
2020-06-02 19:39:54 +00:00
|
|
|
# Comprueba que el mensaje fue enviado desde el sitio o uno
|
|
|
|
# de los sitios permitidos.
|
|
|
|
#
|
|
|
|
# XXX: Este header se puede falsificar de todas formas pero al
|
|
|
|
# menos es una trampa.
|
2020-03-23 20:46:19 +00:00
|
|
|
def site_is_origin?
|
2020-06-02 19:39:54 +00:00
|
|
|
return if site.urls(slash: false).any? { |u| origin.to_s.start_with? u }
|
2020-03-23 20:46:19 +00:00
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
head :precondition_required
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Detecta si la dirección de contacto es válida. Además es
|
|
|
|
# opcional.
|
|
|
|
def from_is_address?
|
2020-05-30 19:43:25 +00:00
|
|
|
return if contact_params[:from].empty?
|
2020-03-23 20:46:19 +00:00
|
|
|
return if EmailAddress.valid? contact_params[:from]
|
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
head :precondition_required
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# No aceptar nada si no dió su consentimiento
|
|
|
|
def gave_consent?
|
2020-05-30 19:43:25 +00:00
|
|
|
return if contact_params[:consent].present?
|
2020-03-23 20:46:19 +00:00
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
head :precondition_required
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
# Los campos que se envían tienen que corresponder con un
|
|
|
|
# formulario de contacto.
|
|
|
|
def form_exists?
|
|
|
|
return if site.form? params[:form]
|
|
|
|
|
|
|
|
head :precondition_required
|
2020-03-25 18:36:06 +00:00
|
|
|
end
|
|
|
|
|
2020-05-30 19:43:25 +00:00
|
|
|
# Encuentra el sitio o devuelve nulo
|
2020-03-23 20:46:19 +00:00
|
|
|
def site
|
2020-03-25 18:36:06 +00:00
|
|
|
@site ||= Site.find_by(name: site_id)
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Parámetros limpios
|
|
|
|
def contact_params
|
2020-05-30 19:43:25 +00:00
|
|
|
@contact_params ||= params.permit(site.form(params[:form]).params)
|
2020-03-23 20:46:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Para poder testear, enviamos un mensaje en el cuerpo de la
|
|
|
|
# respuesta
|
|
|
|
#
|
|
|
|
# @param [Any] el mensaje
|
|
|
|
def body(message)
|
|
|
|
return message.to_s if Rails.env.test?
|
|
|
|
end
|
|
|
|
|
|
|
|
# No queremos informar nada a los spammers, pero en testeo
|
|
|
|
# queremos saber por qué. :no_content oculta el cuerpo.
|
|
|
|
def status
|
|
|
|
Rails.env.test? ? :unprocessable_entity : :no_content
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|