5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-15 10:01:40 +00:00
panel/app/controllers/api/v1/contact_controller.rb

147 lines
4.9 KiB
Ruby
Raw Normal View History

2020-03-23 20:46:19 +00:00
# frozen_string_literal: true
module Api
module V1
# API para formulario de contacto
class ContactController < BaseController
# 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
# 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?
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
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
@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