# frozen_string_literal: true module Api module V1 class PostsController < BaseController # Ver doc/anonymous.md skip_forgery_protection # Protecciones antes de procesar los datos before_action :cookie_is_valid?, unless: :performed? before_action :valid_authenticity_token_in_cookie?, unless: :performed? before_action :site_exists_and_is_anonymous?, unless: :performed? before_action :site_is_origin?, unless: :performed? # Crea un artículo solo si el sitio es invitado, pero antes # tenemos que averiguar varias cosas: # # * la cookie sea válida # * el token anti CSRF es válido # * el sitio existe # * el sitio admite invitades # * el origen de la petición no es el sitio # # TODO: Definir cuáles van a ser las respuestas para cada error # o si simplemente vamos a aceptarlas sin dar feedback. def create # No procesar nada más si ya se aplicaron todos los filtros return if performed? # Redirigir a la URL de agradecimiento redirect_to params[:redirect_to] end private # 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? def cookie_is_valid? unless cookies.encrypted[site_id] && cookies.encrypted[site_id]['expires'] > Time.now.to_i render html: nil, status: :no_content end 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? unless valid_authenticity_token? session, cookies.encrypted[site_id] render html: nil, status: :no_content end end # El sitio existe y soporta colaboracion anónima # # Pedimos el sitio aunque no lo necesitemos para que la consulta # entre en la caché def site_exists_and_is_anonymous? _, anon = site_anon_pair render html: nil, status: :no_content end # El navegador envía la URL del sitio en el encabezado Origin, # queremos comprobar que los datos son enviados desde ahí. def site_is_origin? site, = site_anon_pair unless "https://#{site}" === request.headers['Origin'] render html: nil, status: :no_content end end # Solo soy un atajo def site_id @site_id ||= params[:site_id] end # La consulta más barata que podemos hacer y la reutilizamos para # que esté en la caché def site_anon_pair Site.where(name: site_id, colaboracion_anonima: true) .pluck(:name, :colaboracion_anonima) .first end # Instancia el sitio completo # # XXX: Solo usar después de comprobar que el sitio existe! def site @site ||= Site.find_by(name: site_id) end end end end