summaryrefslogtreecommitdiffstats
path: root/app/lib/admin/system_check/media_privacy_check.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib/admin/system_check/media_privacy_check.rb')
-rw-r--r--app/lib/admin/system_check/media_privacy_check.rb105
1 files changed, 105 insertions, 0 deletions
diff --git a/app/lib/admin/system_check/media_privacy_check.rb b/app/lib/admin/system_check/media_privacy_check.rb
new file mode 100644
index 00000000000..1df05b120ea
--- /dev/null
+++ b/app/lib/admin/system_check/media_privacy_check.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+class Admin::SystemCheck::MediaPrivacyCheck < Admin::SystemCheck::BaseCheck
+ include RoutingHelper
+
+ def skip?
+ !current_user.can?(:view_devops)
+ end
+
+ def pass?
+ check_media_uploads!
+ @failure_message.nil?
+ end
+
+ def message
+ Admin::SystemCheck::Message.new(@failure_message, @failure_value, @failure_action, true)
+ end
+
+ private
+
+ def check_media_uploads!
+ if Rails.configuration.x.use_s3
+ check_media_listing_inaccessible_s3!
+ else
+ check_media_listing_inaccessible!
+ end
+ end
+
+ def check_media_listing_inaccessible!
+ full_url = full_asset_url(media_attachment.file.url(:original, false))
+
+ # Check if we can list the uploaded file. If true, that's an error
+ directory_url = Addressable::URI.parse(full_url)
+ directory_url.query = nil
+ filename = directory_url.path.gsub(%r{.*/}, '')
+ directory_url.path = directory_url.path.gsub(%r{/[^/]+\Z}, '/')
+ Request.new(:get, directory_url, allow_local: true).perform do |res|
+ if res.truncated_body&.include?(filename)
+ @failure_message = use_storage? ? :upload_check_privacy_error_object_storage : :upload_check_privacy_error
+ @failure_action = 'https://docs.joinmastodon.org/admin/optional/object-storage/#FS'
+ end
+ end
+ rescue
+ nil
+ end
+
+ def check_media_listing_inaccessible_s3!
+ urls_to_check = []
+ paperclip_options = Paperclip::Attachment.default_options
+ s3_protocol = paperclip_options[:s3_protocol]
+ s3_host_alias = paperclip_options[:s3_host_alias]
+ s3_host_name = paperclip_options[:s3_host_name]
+ bucket_name = paperclip_options.dig(:s3_credentials, :bucket)
+
+ urls_to_check << "#{s3_protocol}://#{s3_host_alias}/" if s3_host_alias.present?
+ urls_to_check << "#{s3_protocol}://#{s3_host_name}/#{bucket_name}/"
+ urls_to_check.uniq.each do |full_url|
+ check_s3_listing!(full_url)
+ break if @failure_message.present?
+ end
+ rescue
+ nil
+ end
+
+ def check_s3_listing!(full_url)
+ bucket_url = Addressable::URI.parse(full_url)
+ bucket_url.path = bucket_url.path.delete_suffix(media_attachment.file.path(:original))
+ bucket_url.query = "max-keys=1&x-random=#{SecureRandom.hex(10)}"
+ Request.new(:get, bucket_url, allow_local: true).perform do |res|
+ if res.truncated_body&.include?('ListBucketResult')
+ @failure_message = :upload_check_privacy_error_object_storage
+ @failure_action = 'https://docs.joinmastodon.org/admin/optional/object-storage/#S3'
+ end
+ end
+ end
+
+ def media_attachment
+ @media_attachment ||= begin
+ attachment = Account.representative.media_attachments.first
+ if attachment.present?
+ attachment.touch # rubocop:disable Rails/SkipsModelValidations
+ attachment
+ else
+ create_test_attachment!
+ end
+ end
+ end
+
+ def create_test_attachment!
+ Tempfile.create(%w(test-upload .jpg), binmode: true) do |tmp_file|
+ tmp_file.write(
+ Base64.decode64(
+ '/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' \
+ 'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' \
+ 'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' \
+ 'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' \
+ 'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' \
+ 'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q=='
+ )
+ )
+ tmp_file.flush
+ Account.representative.media_attachments.create!(file: tmp_file)
+ end
+ end
+end