debian-mirror-gitlab/app/uploaders/gitlab_uploader.rb

184 lines
4.7 KiB
Ruby
Raw Normal View History

2018-11-08 19:23:39 +05:30
# frozen_string_literal: true
2017-08-17 22:00:37 +05:30
class GitlabUploader < CarrierWave::Uploader::Base
2020-04-08 14:13:33 +05:30
include ContentTypeWhitelist::Concern
2018-03-17 18:26:18 +05:30
class_attribute :options
2017-08-17 22:00:37 +05:30
2020-11-05 12:06:23 +05:30
PROTECTED_METHODS = %i(filename cache_dir work_dir store_dir).freeze
ObjectNotReadyError = Class.new(StandardError)
2018-03-17 18:26:18 +05:30
class << self
# DSL setter
def storage_options(options)
self.options = options
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
def root
options.storage_path
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
# represent the directory namespacing at the class level
def base_dir
options.fetch('base_dir', '')
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
def file_storage?
storage == CarrierWave::Storage::File
end
def absolute_path(upload_record)
File.join(root, upload_record.path)
end
2022-07-23 23:45:48 +05:30
def version(*args, **kwargs, &block)
# CarrierWave uploaded file "versions" are not tracked in the uploads
# table. Because of this they won't get replicated to Geo secondaries.
# If we ever want to use versions, we need to fix that first. Also see
# https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1757.
raise "using CarrierWave alternate file version is not supported"
end
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
storage_options Gitlab.config.uploads
2017-08-17 22:00:37 +05:30
delegate :base_dir, :file_storage?, to: :class
2020-11-05 12:06:23 +05:30
before :cache, :protect_from_path_traversal!
2018-03-17 18:26:18 +05:30
def initialize(model, mounted_as = nil, **uploader_context)
super(model, mounted_as)
end
2017-09-10 17:25:29 +05:30
def file_cache_storage?
cache_storage.is_a?(CarrierWave::Storage::File)
end
2017-08-17 22:00:37 +05:30
def move_to_cache
2018-03-17 18:26:18 +05:30
file_storage?
2017-08-17 22:00:37 +05:30
end
def move_to_store
2018-03-17 18:26:18 +05:30
file_storage?
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
def exists?
file.present?
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
def cache_dir
File.join(root, base_dir, 'tmp/cache')
2017-08-17 22:00:37 +05:30
end
2017-09-10 17:25:29 +05:30
def work_dir
2018-03-17 18:26:18 +05:30
File.join(root, base_dir, 'tmp/work')
2017-09-10 17:25:29 +05:30
end
def filename
super || file&.filename
end
2018-12-05 23:21:45 +05:30
def relative_path
return path if pathname.relative?
pathname.relative_path_from(Pathname.new(root))
end
2018-03-17 18:26:18 +05:30
def model_valid?
!!model
end
2018-05-09 12:01:36 +05:30
def local_url
File.join('/', self.class.base_dir, dynamic_segment, filename)
end
2018-11-18 11:00:15 +05:30
def cached_size
size
end
def open
stream =
if file_storage?
File.open(path, "rb") if path
else
::Gitlab::HttpIO.new(url, cached_size) if url
end
return unless stream
return stream unless block_given?
begin
yield(stream)
ensure
stream.close
end
end
2019-12-21 20:55:43 +05:30
# Used to replace an existing upload with another +file+ without modifying stored metadata
# Use this method only to repair/replace an existing upload, or to upload to a Geo secondary node
#
# @param [CarrierWave::SanitizedFile] file that will replace existing upload
# @return CarrierWave::SanitizedFile
def replace_file_without_saving!(file)
raise ArgumentError, 'should be a CarrierWave::SanitizedFile' unless file.is_a? CarrierWave::SanitizedFile
storage.store!(file)
end
2021-01-29 00:20:46 +05:30
def url_or_file_path(url_options = {})
if file_storage?
'file://' + path
else
url(url_options)
end
end
2017-09-10 17:25:29 +05:30
private
2018-03-17 18:26:18 +05:30
# Designed to be overridden by child uploaders that have a dynamic path
# segment -- that is, a path that changes based on mutable attributes of its
# associated model
2018-05-09 12:01:36 +05:30
#
# For example, `FileUploader` builds the storage path based on the associated
# project model's `path_with_namespace` value, which can change when the
# project or its containing namespace is moved or renamed.
2020-11-05 12:06:23 +05:30
#
# When implementing this method, raise `ObjectNotReadyError` if the model
# does not yet exist, as it will be tested in `#protect_from_path_traversal!`
2018-03-17 18:26:18 +05:30
def dynamic_segment
raise(NotImplementedError)
end
2017-09-10 17:25:29 +05:30
# To prevent files from moving across filesystems, override the default
# implementation:
# http://github.com/carrierwaveuploader/carrierwave/blob/v1.0.0/lib/carrierwave/uploader/cache.rb#L181-L183
def workfile_path(for_file = original_filename)
# To be safe, keep this directory outside of the the cache directory
# because calling CarrierWave.clean_cache_files! will remove any files in
# the cache directory.
2018-03-17 18:26:18 +05:30
File.join(work_dir, cache_id, version_name.to_s, for_file)
2017-09-10 17:25:29 +05:30
end
2018-12-05 23:21:45 +05:30
def pathname
@pathname ||= Pathname.new(path)
end
2020-11-05 12:06:23 +05:30
# Protect against path traversal attacks
# This takes a list of methods to test for path traversal, e.g. ../../
# and checks each of them. This uses `.send` so that any potential errors
# don't block the entire set from being tested.
#
# @param [CarrierWave::SanitizedFile]
# @return [Nil]
# @raise [Gitlab::Utils::PathTraversalAttackError]
def protect_from_path_traversal!(file)
PROTECTED_METHODS.each do |method|
Gitlab::Utils.check_path_traversal!(self.send(method)) # rubocop: disable GitlabSecurity/PublicSend
rescue ObjectNotReadyError
# Do nothing. This test was attempted before the file was ready for that method
end
end
2017-08-17 22:00:37 +05:30
end