0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-05 19:53:47 +08:00
discourse/app/services/upcoming_changes/toggle.rb
Isaac Janzen 1599c4512b
FIX: Restrict upcoming-changes toggle to registered upcoming-change settings (#42175)
## Summary

The admin upcoming-changes toggle endpoint accepted any public
zero-argument SiteSetting method as a valid setting name. The fix
restricts eligibility to registered upcoming-change metadata by checking
UpcomingChanges.exists? instead of SiteSetting.respond_to?, preventing
invocation of non-setting methods like notify_changed!.

## Source

- Patch Triage: https://patch.discourse.org/patch-triage/1508

Co-authored-by: discourse-patch-triage
<272280883+discourse-patch-triage[bot]@users.noreply.github.com>
2026-07-30 09:28:04 -05:00

91 lines
2.5 KiB
Ruby
Vendored

# frozen_string_literal: true
class UpcomingChanges::Toggle
include Service::Base
# For cases like the UpcomingChanges::Promote where we don't want to log
# the change again since it's already being logged there.
options { attribute :log_change, default: true }
params do
attribute :setting_name, :symbol
attribute :enabled, :boolean
validates :setting_name, presence: true
validates :enabled, inclusion: [true, false]
def upcoming_change_event
enabled ? :upcoming_change_enabled : :upcoming_change_disabled
end
end
policy :current_user_is_admin
policy :setting_is_available
policy :allowed_enabled_for_target
transaction { step :toggle }
step :clear_groups_if_groups_not_allowed
only_if(:should_log_change) do
step :log_change
step :log_event
end
step :trigger_event
private
def current_user_is_admin(guardian:)
guardian.is_admin?
end
def setting_is_available(params:)
UpcomingChanges.exists?(params.setting_name)
end
def toggle(params:, guardian:, options:)
context[:previous_value] = SiteSetting.public_send(params.setting_name)
SiteSetting.send("#{params.setting_name}=", params.enabled)
end
def allowed_enabled_for_target(params:)
return true if !params.enabled
# When enabling with no groups configured, the resulting target is "everyone".
# Otherwise the group target is validated by SiteSetting::UpsertGroups.
return true if SiteSettingGroup.exists?(name: params.setting_name)
UpcomingChanges.target_allowed?(params.setting_name, :everyone)
end
def clear_groups_if_groups_not_allowed(params:)
return if UpcomingChanges.groups_target_allowed?(params.setting_name)
SiteSettingGroup.find_by(name: params.setting_name)&.destroy!
SiteSetting.refresh_site_setting_group_ids!
end
def should_log_change(options:)
options.log_change
end
def log_change(params:, guardian:, options:)
StaffActionLogger.new(guardian.user).log_upcoming_change_toggle(
params.setting_name,
context[:previous_value],
params.enabled,
{ context: I18n.t("staff_action_logs.upcoming_changes.log_manually_toggled") },
)
end
def log_event(params:, guardian:, options:)
UpcomingChangeEvent.create!(
event_type: params.enabled ? :manual_opt_in : :manual_opt_out,
upcoming_change_name: params.setting_name,
acting_user: guardian.user,
)
end
def trigger_event(params:)
DiscourseEvent.trigger(params.upcoming_change_event, params.setting_name)
end
end