From 8ff5626e9968407d5b12f5e9a77fd3cb739ff77d Mon Sep 17 00:00:00 2001 From: f Date: Sat, 16 Mar 2024 15:12:55 -0300 Subject: [PATCH] feat: no permitir entrar al evento si falla la request --- app/controllers/activity_pubs_controller.rb | 14 +++--- .../actor_moderations_controller.rb | 13 ++--- .../instance_moderations_controller.rb | 13 ++--- app/models/activity_pub.rb | 21 ++++---- app/models/actor_moderation.rb | 32 +++++-------- app/models/concerns/aasm_events_concern.rb | 29 ++++------- app/models/fediblock_state.rb | 48 ++++++++++--------- app/models/instance_moderation.rb | 28 ++++------- 8 files changed, 88 insertions(+), 110 deletions(-) diff --git a/app/controllers/activity_pubs_controller.rb b/app/controllers/activity_pubs_controller.rb index edece8f8..225311c2 100644 --- a/app/controllers/activity_pubs_controller.rb +++ b/app/controllers/activity_pubs_controller.rb @@ -9,14 +9,16 @@ class ActivityPubsController < ApplicationController authorize activity_pub activity_pub.update(remote_flag_params(activity_pub)) if event == :report - activity_pub.public_send(:"#{event}!") if activity_pub.public_send(:"may_#{event}?") - flash[:success] = I18n.t("activity_pubs.#{event}.success") - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + message = + if activity_pub.public_send(:"may_#{event}?") && activity_pub.public_send(:"#{event}!") + :success + else + :error + end + + flash[message] = I18n.t("activity_pubs.#{event}.#{message}") - flash[:error] = I18n.t("activity_pubs.#{event}.error") - ensure redirect_to_moderation_queue! end end diff --git a/app/controllers/actor_moderations_controller.rb b/app/controllers/actor_moderations_controller.rb index 6316a4cf..cb7a63a9 100644 --- a/app/controllers/actor_moderations_controller.rb +++ b/app/controllers/actor_moderations_controller.rb @@ -17,14 +17,15 @@ class ActorModerationsController < ApplicationController # Crea una RemoteFlag si se envían los parámetros adecuados actor_moderation.update(remote_flag_params(actor_moderation)) if actor_event == :report - actor_moderation.public_send(:"#{actor_event}!") if actor_moderation.public_send(:"may_#{actor_event}?") + message = + if actor_moderation.public_send(:"may_#{actor_event}?") && actor_moderation.public_send(:"#{actor_event}!") + :success + else + :error + end - flash[:success] = I18n.t("actor_moderations.#{actor_event}.success") - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + flash[message] = I18n.t("actor_moderations.#{actor_event}.#{message}") - flash[:error] = I18n.t("actor_moderations.#{actor_event}.error") - ensure redirect_to_moderation_queue! end end diff --git a/app/controllers/instance_moderations_controller.rb b/app/controllers/instance_moderations_controller.rb index 06f5cfc1..13d7f428 100644 --- a/app/controllers/instance_moderations_controller.rb +++ b/app/controllers/instance_moderations_controller.rb @@ -8,14 +8,15 @@ class InstanceModerationsController < ApplicationController define_method(event) do authorize instance_moderation - instance_moderation.public_send(:"#{event}!") if instance_moderation.public_send(:"may_#{event}?") + message = + if instance_moderation.public_send(:"may_#{event}?") && instance_moderation.public_send(:"#{event}!") + :success + else + :error + end - flash[:success] = I18n.t("instance_moderations.#{event}.success") - rescue Exception => e - ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params.permit!.to_h }) + flash[message] = I18n.t("instance_moderations.#{event}.#{message}") - flash[:error] = I18n.t("instance_moderations.#{event}.error") - ensure redirect_to_moderation_queue! end end diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb index cae054cd..8142ecfb 100644 --- a/app/models/activity_pub.rb +++ b/app/models/activity_pub.rb @@ -47,14 +47,19 @@ class ActivityPub < ApplicationRecord # Todavía no hay una decisión sobre el objeto state :paused, initial: true # Le usuarie aprobó el objeto - state :approved + state :approved, before_enter: :allow_remotely! # Le usuarie rechazó el objeto - state :rejected + state :rejected, before_enter: :reject_remotely! # Le usuarie reportó el objeto state :reported # Le actore eliminó el objeto state :removed + # Gestionar todos los errores + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: self.id, activity: activities.first.uri }) + end + # Se puede volver a pausa en caso de actualización remota, para # revisar los cambios. event :pause do @@ -67,7 +72,7 @@ class ActivityPub < ApplicationRecord event :remove do transitions to: :removed - before do + after do next if object.blank? object.update(content: {}) unless object.content.empty? @@ -79,26 +84,18 @@ class ActivityPub < ApplicationRecord # webhook a modo de confirmación. event :approve do transitions from: %i[paused], to: :approved - - before do - allow_remotely! - end end # La actividad fue rechazada event :reject do transitions from: %i[paused], to: :rejected - - before do - reject_remotely! - end end # Solo podemos reportarla luego de rechazarla event :report do transitions from: :rejected, to: :reported - before do + after do ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? end end diff --git a/app/models/actor_moderation.rb b/app/models/actor_moderation.rb index 3b183a4f..c566b844 100644 --- a/app/models/actor_moderation.rb +++ b/app/models/actor_moderation.rb @@ -2,8 +2,8 @@ # Mantiene la relación entre Site y Actor class ActorModeration < ApplicationRecord - IGNORED_EVENTS = %i[remove] - IGNORED_STATES = %i[removed] + IGNORED_EVENTS = %i[remove].freeze + IGNORED_STATES = %i[removed].freeze include AASM @@ -14,38 +14,30 @@ class ActorModeration < ApplicationRecord accepts_nested_attributes_for :remote_flag aasm do - state :paused, initial: true - state :allowed - state :blocked + state :paused, initial: true, before_enter: :pause_remotely! + state :allowed, before_enter: :allow_remotely! + state :blocked, before_enter: :block_remotely! state :reported state :removed + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, actor: actor.uri, actor_moderation: id }) + end + event :pause do transitions from: %i[allowed blocked reported], to: :paused - - before do - pause_remotely! - end end # Al permitir una cuenta no se permiten todos los comentarios # pendientes de moderación que ya hizo. event :allow do transitions from: %i[paused blocked reported], to: :allowed - - before do - allow_remotely! - end end # Al bloquear una cuenta no se bloquean todos los comentarios # pendientes de moderación que hizo. event :block do transitions from: %i[paused allowed], to: :blocked - - before do - block_remotely! - end end # Al reportar, necesitamos asociar una RemoteFlag para poder @@ -53,7 +45,7 @@ class ActorModeration < ApplicationRecord event :report do transitions from: %i[blocked], to: :reported - before do + after do ActivityPub::RemoteFlagJob.perform_later(remote_flag: remote_flag) if remote_flag.waiting? end end @@ -63,8 +55,8 @@ class ActorModeration < ApplicationRecord event :remove do transitions to: :removed - before do - site.activity_pubs.where(actor_id: self.actor_id).remove_all! + after do + site.activity_pubs.where(actor_id: actor_id).remove_all! end end end diff --git a/app/models/concerns/aasm_events_concern.rb b/app/models/concerns/aasm_events_concern.rb index 4de5f748..788e9e1a 100644 --- a/app/models/concerns/aasm_events_concern.rb +++ b/app/models/concerns/aasm_events_concern.rb @@ -16,7 +16,7 @@ module AasmEventsConcern # # @return [Array] def self.transitionable_events(current_state) - self.events.select do |event| + events.select do |event| aasm.events.find { |x| x.name == event }.transitions_from_state? current_state end end @@ -32,19 +32,15 @@ module AasmEventsConcern # scope actual. # # @return [Bool] Si hubo al menos un error, devuelve false. - self.aasm.events.map(&:name).each do |event| + aasm.events.map(&:name).each do |event| define_singleton_method(:"#{event}_all!") do - success = true + successes = [] - self.find_each do |object| - object.public_send(:"#{event}!") if object.public_send(:"may_#{event}?") - rescue Exception => e - success = false - - notify_exception! e, object + find_each do |object| + successes << (object.public_send(:"may_#{event}?") && object.public_send(:"#{event}!")) end - success + successes.all? end # Ejecuta la transición del evento en la base de datos sin @@ -53,20 +49,13 @@ module AasmEventsConcern # # @return [Integer] Registros modificados define_singleton_method(:"#{event}_all_without_callbacks!") do - aasm_event = self.aasm.events.find { |e| e.name == event } + aasm_event = aasm.events.find { |e| e.name == event } to_state = aasm_event.transitions.map(&:to).first from_states = aasm_event.transitions.map(&:from) - self.unscope(where: :aasm_state).where(aasm_state: from_states).update_all(aasm_state: to_state, updated_at: Time.now) + unscope(where: :aasm_state).where(aasm_state: from_states).update_all(aasm_state: to_state, + updated_at: Time.now) end end - - # Envía notificación de errores - # - # @param exception [Exception] - # @param record [ApplicationRecord] - def self.notify_exception!(exception, record) - ExceptionNotifier.notify_exception(exception, data: { record_type: record.class.name, record_id: record.id }) - end end end diff --git a/app/models/fediblock_state.rb b/app/models/fediblock_state.rb index 9dde1db3..a6faa14f 100644 --- a/app/models/fediblock_state.rb +++ b/app/models/fediblock_state.rb @@ -20,20 +20,15 @@ class FediblockState < ApplicationRecord # Aunque queramos las listas habilitadas por defecto, tenemos que # habilitarlas luego de crearlas para poder generar la lista de # bloqueo en la Social Inbox. - state :disabled, initial: true - state :enabled + state :disabled, initial: true, before_enter: :disable_remotely_and_pause_instances! + state :enabled, before_enter: :enable_remotely_and_block_instances! + + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, fediblock: id }) + end event :enable do transitions from: :disabled, to: :enabled - - before do - # Bloquear todos las instancias de este Fediblock - enable_remotely! list_names(fediblock.hostnames) - - # Luego esta tarea crea las que falten e ignora las que ya se - # bloquearon. - ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames, perform_remotely: false) - end end # Al deshabilitar, las listas pasan a modo pausa, a menos que estén @@ -44,22 +39,31 @@ class FediblockState < ApplicationRecord # de list_names event :disable do transitions from: :enabled, to: :disabled - - before do - # Deshabilitar todas las instancias que no estén habilitadas por - # otros fediblocks - disable_remotely! list_names(unique_hostnames) - - # Pausar todas las moderaciones de las instancias que no estén - # bloqueadas por otros fediblocks. - instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids - site.instance_moderations.where(instance_id: instance_ids).pause_all_without_callbacks! - end end end private + def enable_remotely_and_block_instances! + # Bloquear todos las instancias de este Fediblock + enable_remotely! list_names(fediblock.hostnames) + + # Luego esta tarea crea las que falten e ignora las que ya se + # bloquearon. + ActivityPub::InstanceModerationJob.perform_now(site: site, hostnames: fediblock.hostnames, perform_remotely: false) + end + + def disable_remotely_and_pause_instances! + # Deshabilitar todas las instancias que no estén habilitadas por + # otros fediblocks + disable_remotely! list_names(unique_hostnames) + + # Pausar todas las moderaciones de las instancias que no estén + # bloqueadas por otros fediblocks. + instance_ids = ActivityPub::Instance.where(hostname: unique_hostnames).ids + site.instance_moderations.where(instance_id: instance_ids).pause_all_without_callbacks! + end + # Devuelve los hostnames únicos a esta instancia. # # @return [Array] diff --git a/app/models/instance_moderation.rb b/app/models/instance_moderation.rb index 9cb6ffdc..7c76262e 100644 --- a/app/models/instance_moderation.rb +++ b/app/models/instance_moderation.rb @@ -2,8 +2,8 @@ # Mantiene el registro de relaciones entre sitios e instancias class InstanceModeration < ApplicationRecord - IGNORED_EVENTS = [] - IGNORED_STATES = [] + IGNORED_EVENTS = [].freeze + IGNORED_STATES = [].freeze include AASM @@ -11,38 +11,30 @@ class InstanceModeration < ApplicationRecord belongs_to :instance, class_name: 'ActivityPub::Instance' aasm do - state :paused, initial: true - state :allowed - state :blocked + state :paused, initial: true, before_enter: :pause_remotely! + state :allowed, before_enter: :allow_remotely! + state :blocked, before_enter: :block_remotely! + + error_on_all_events do |e| + ExceptionNotifier.notify_exception(e, data: { site: site.name, instance: instance.hostname, instance_moderation: id }) + end # Al volver la instancia a pausa no cambiamos el estado de # moderación de actores pre-existente. event :pause do transitions from: %i[allowed blocked], to: :paused - - before do - pause_remotely! - end end # Al permitir, solo bloqueamos la instancia, sin modificar el estado # de les actores y comentarios retroactivamente. event :allow do transitions from: %i[paused blocked], to: :allowed - - before do - allow_remotely! - end end # Al bloquear, solo bloqueamos la instancia, sin modificar el estado # de les actores y comentarios retroactivamente. event :block do transitions from: %i[paused allowed], to: :blocked - - before do - block_remotely! - end end end @@ -51,7 +43,7 @@ class InstanceModeration < ApplicationRecord # @return [Array] def actor_ids - ActivityPub::Actor.where(instance_id: self.instance_id).ids + ActivityPub::Actor.where(instance_id: instance_id).ids end # Elimina la instancia de todas las listas