about summary refs log tree commit diff
path: root/app/services/post_status_service.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/post_status_service.rb')
-rw-r--r--app/services/post_status_service.rb28
1 files changed, 25 insertions, 3 deletions
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index bcda001f5..77527f2db 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -6,6 +6,15 @@ class PostStatusService < BaseService
 
   MIN_SCHEDULE_OFFSET = 5.minutes.freeze
 
+  class UnexpectedMentionsError < StandardError
+    attr_reader :accounts
+
+    def initialize(message, accounts)
+      super(message)
+      @accounts = accounts
+    end
+  end
+
   # Post a text status update, fetch and notify remote users mentioned
   # @param [Account] account Account from which to post
   # @param [Hash] options
@@ -21,6 +30,7 @@ class PostStatusService < BaseService
   # @option [Doorkeeper::Application] :application
   # @option [String] :idempotency Optional idempotency key
   # @option [Boolean] :with_rate_limit
+  # @option [Enumerable] :allowed_mentions Optional array of expected mentioned account IDs, raises `UnexpectedMentionsError` if unexpected accounts end up in mentions
   # @return [Status]
   def call(account, options = {})
     @account     = account
@@ -72,14 +82,27 @@ class PostStatusService < BaseService
   end
 
   def process_status!
+    @status = @account.statuses.new(status_attributes)
+    process_mentions_service.call(@status, save_records: false)
+    safeguard_mentions!(@status)
+
     # The following transaction block is needed to wrap the UPDATEs to
     # the media attachments when the status is created
-
     ApplicationRecord.transaction do
-      @status = @account.statuses.create!(status_attributes)
+      @status.save!
     end
   end
 
+  def safeguard_mentions!(status)
+    return if @options[:allowed_mentions].nil?
+    expected_account_ids = @options[:allowed_mentions].map(&:to_i)
+
+    unexpected_accounts = status.mentions.map(&:account).to_a.reject { |mentioned_account| expected_account_ids.include?(mentioned_account.id) }
+    return if unexpected_accounts.empty?
+
+    raise UnexpectedMentionsError.new('Post would be sent to unexpected accounts', unexpected_accounts)
+  end
+
   def schedule_status!
     status_for_validation = @account.statuses.build(status_attributes)
 
@@ -102,7 +125,6 @@ class PostStatusService < BaseService
 
   def postprocess_status!
     process_hashtags_service.call(@status)
-    process_mentions_service.call(@status)
     Trends.tags.register(@status)
     LinkCrawlWorker.perform_async(@status.id)
     DistributionWorker.perform_async(@status.id)