uploads: refactor media asset validation logic.

Refactor the upload validation logic to not depend on the current user.
Fixes several broken upload tests.
This commit is contained in:
evazion
2022-09-15 04:51:48 -05:00
parent bfe2eabc6d
commit 0a5ebcc69d
4 changed files with 26 additions and 26 deletions

View File

@@ -105,6 +105,11 @@ class MediaFile
dimensions.second
end
# @return [Integer] the resolution of the file
def resolution
width * height
end
# @return [String] the MD5 hash of the file, as a hex string.
def md5
Digest::MD5.file(file.path).hexdigest

View File

@@ -18,6 +18,7 @@ class PostReplacementProcessor
if media_file.md5 == post.md5
media_asset = post.media_asset
else
MediaAsset.validate_media_file!(media_file, replacement.creator)
media_asset = MediaAsset.upload!(media_file)
end

View File

@@ -6,6 +6,7 @@ class MediaAsset < ApplicationRecord
FILE_TYPES = %w[jpg png gif mp4 webm swf zip]
FILE_KEY_LENGTH = 9
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
MAX_FILE_SIZE = Danbooru.config.max_file_size.to_i
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
MAX_IMAGE_RESOLUTION = Danbooru.config.max_image_resolution
MAX_IMAGE_WIDTH = Danbooru.config.max_image_width
@@ -45,10 +46,7 @@ class MediaAsset < ApplicationRecord
validates :md5, uniqueness: { conditions: -> { where(status: [:processing, :active]) } }, if: :md5_changed?
validates :file_ext, inclusion: { in: FILE_TYPES, message: "File is not an image or video" }
validates :file_size, numericality: { less_than_or_equal_to: Danbooru.config.max_file_size, message: ->(asset, _) { "too large (size: #{asset.file_size.to_formatted_s(:human_size)}; max size: #{Danbooru.config.max_file_size.to_formatted_s(:human_size)})" } }
validates :file_key, length: { is: FILE_KEY_LENGTH }, uniqueness: true, if: :file_key_changed?
validate :validate_duration, on: :create
validate :validate_resolution, on: :create
before_create :initialize_file_key
@@ -248,8 +246,6 @@ class MediaAsset < ApplicationRecord
def upload!(media_file, &block)
media_file = MediaFile.open(media_file) unless media_file.is_a?(MediaFile)
raise Error, "File is corrupt" if media_file.is_corrupt?
media_asset = create!(file: media_file, status: :processing)
yield media_asset if block_given?
@@ -294,6 +290,24 @@ class MediaAsset < ApplicationRecord
media_asset&.update!(status: :failed)
raise
end
def validate_media_file!(media_file, uploader)
if !media_file.file_ext.to_s.in?(FILE_TYPES)
raise Error, "File is not an image or video"
elsif media_file.is_corrupt?
raise Error, "File is corrupt"
elsif media_file.file_size > MAX_FILE_SIZE
raise Error, "File size too large (size: #{media_file.file_size.to_formatted_s(:human_size)}; max size: #{MAX_FILE_SIZE.to_formatted_s(:human_size)})"
elsif media_file.resolution > MAX_IMAGE_RESOLUTION
raise Error, "Image resolution is too large (resolution: #{(media_file.resolution / 1_000_000.0).round(1)} megapixels (#{media_file.width}x#{media_file.height}); max: #{MAX_IMAGE_RESOLUTION / 1_000_000} megapixels)"
elsif media_file.width > MAX_IMAGE_WIDTH
raise Error, "Image width is too large (width: #{media_file.width}; max width: #{MAX_IMAGE_WIDTH})"
elsif media_file.height > MAX_IMAGE_HEIGHT
raise Error, "Image height is too large (height: #{media_file.height}; max height: #{MAX_IMAGE_HEIGHT})"
elsif media_file.duration.to_i > MAX_VIDEO_DURATION && !uploader.is_admin?
raise Error, "Duration must be less than #{MAX_VIDEO_DURATION} seconds"
end
end
end
def file=(file_or_path)
@@ -397,27 +411,6 @@ class MediaAsset < ApplicationRecord
end
end
concerning :ValidationMethods do
def validate_resolution
resolution = image_width * image_height
if resolution > MAX_IMAGE_RESOLUTION
errors.add(:base, "Image resolution is too large (resolution: #{(resolution / 1_000_000.0).round(1)} megapixels (#{image_width}x#{image_height}); max: #{MAX_IMAGE_RESOLUTION / 1_000_000} megapixels)")
elsif image_width > MAX_IMAGE_WIDTH
errors.add(:image_width, "is too large (width: #{image_width}; max width: #{MAX_IMAGE_WIDTH})")
elsif image_height > MAX_IMAGE_HEIGHT
errors.add(:image_height, "is too large (height: #{image_height}; max height: #{MAX_IMAGE_HEIGHT})")
end
end
def validate_duration
return if CurrentUser.user.is_admin?
if duration.to_i > MAX_VIDEO_DURATION
errors.add(:base, "duration must be less than #{MAX_VIDEO_DURATION} seconds")
end
end
end
def self.generate_file_key
loop do
key = SecureRandom.send(:choose, [*"0".."9", *"A".."Z", *"a".."z"], FILE_KEY_LENGTH) # base62

View File

@@ -101,6 +101,7 @@ class UploadMediaAsset < ApplicationRecord
media_file = source_extractor.download_file!(source_url)
end
MediaAsset.validate_media_file!(media_file, upload.uploader)
MediaAsset.upload!(media_file) do |media_asset|
update!(media_asset: media_asset)
end