2018-12-05 23:21:45 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
module UploadsActions
|
2020-01-01 13:55:28 +05:30
|
|
|
extend ActiveSupport::Concern
|
2018-03-17 18:26:18 +05:30
|
|
|
include Gitlab::Utils::StrongMemoize
|
2018-05-09 12:01:36 +05:30
|
|
|
include SendFileUpload
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
UPLOAD_MOUNTS = %w[avatar attachment file logo pwa_icon header_logo favicon screenshot].freeze
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
included do
|
|
|
|
prepend_before_action :set_request_format_from_path_extension
|
2020-03-07 23:17:34 +05:30
|
|
|
rescue_from FileUploader::InvalidSecret, with: :render_404
|
2023-07-09 08:55:56 +05:30
|
|
|
|
|
|
|
rescue_from ::Gitlab::Utils::PathTraversalAttackError do
|
|
|
|
head :bad_request
|
|
|
|
end
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
def create
|
2019-02-15 15:39:39 +05:30
|
|
|
uploader = UploadService.new(model, params[:file], uploader_class).execute
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
respond_to do |format|
|
2019-02-15 15:39:39 +05:30
|
|
|
if uploader
|
2017-08-17 22:00:37 +05:30
|
|
|
format.json do
|
2019-02-15 15:39:39 +05:30
|
|
|
render json: { link: uploader.to_h }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
else
|
|
|
|
format.json do
|
2019-07-07 11:18:12 +05:30
|
|
|
render json: _('Invalid file.'), status: :unprocessable_entity
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
# This should either
|
|
|
|
# - send the file directly
|
|
|
|
# - or redirect to its URL
|
|
|
|
#
|
2017-08-17 22:00:37 +05:30
|
|
|
def show
|
2023-07-09 08:55:56 +05:30
|
|
|
Gitlab::Utils.check_path_traversal!(params[:filename])
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
return render_404 unless uploader&.exists?
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
ttl, directives = *cache_settings
|
|
|
|
ttl ||= 0
|
|
|
|
directives ||= { private: true, must_revalidate: true }
|
|
|
|
|
|
|
|
expires_in ttl, directives
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
file_uploader = [uploader, *uploader.versions.values].find do |version|
|
|
|
|
version.filename == params[:filename]
|
|
|
|
end
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
return render_404 unless file_uploader
|
2018-11-08 19:23:39 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
workhorse_set_content_type!
|
2020-01-01 13:55:28 +05:30
|
|
|
send_upload(file_uploader, attachment: file_uploader.filename, disposition: content_disposition)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
def authorize
|
|
|
|
set_workhorse_internal_api_content_type
|
|
|
|
|
|
|
|
authorized = uploader_class.workhorse_authorize(
|
|
|
|
has_length: false,
|
|
|
|
maximum_size: Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i)
|
|
|
|
|
|
|
|
render json: authorized
|
2018-11-20 20:47:30 +05:30
|
|
|
rescue SocketError
|
2019-07-07 11:18:12 +05:30
|
|
|
render json: _("Error uploading file"), status: :internal_server_error
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
private
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
# Based on ActionDispatch::Http::MimeNegotiation. We have an
|
|
|
|
# initializer that monkey-patches this method out (so that repository
|
|
|
|
# paths don't guess a format based on extension), but we do want this
|
|
|
|
# behavior when serving uploads.
|
|
|
|
def set_request_format_from_path_extension
|
|
|
|
path = request.headers['action_dispatch.original_path'] || request.headers['PATH_INFO']
|
|
|
|
|
2023-03-04 22:38:38 +05:30
|
|
|
return unless match = path&.match(/\.(\w+)\z/)
|
2020-01-01 13:55:28 +05:30
|
|
|
|
2023-03-04 22:38:38 +05:30
|
|
|
format = Mime[match.captures.first]
|
|
|
|
|
|
|
|
request.format = format.symbol if format
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def content_disposition
|
|
|
|
if uploader.embeddable? || uploader.pdf?
|
|
|
|
'inline'
|
|
|
|
else
|
|
|
|
'attachment'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def uploader_class
|
|
|
|
raise NotImplementedError
|
|
|
|
end
|
|
|
|
|
|
|
|
def upload_mount
|
|
|
|
mounted_as = params[:mounted_as]
|
|
|
|
mounted_as if UPLOAD_MOUNTS.include?(mounted_as)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def uploader_mounted?
|
|
|
|
upload_model_class < CarrierWave::Mount::Extension && !upload_mount.nil?
|
|
|
|
end
|
|
|
|
|
|
|
|
def uploader
|
2023-03-04 22:38:38 +05:30
|
|
|
if uploader_mounted?
|
|
|
|
model.public_send(upload_mount) # rubocop:disable GitlabSecurity/PublicSend
|
|
|
|
else
|
|
|
|
build_uploader_from_upload || build_uploader_from_params
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
end
|
2023-03-04 22:38:38 +05:30
|
|
|
strong_memoize_attr :uploader
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2018-03-17 18:26:18 +05:30
|
|
|
def build_uploader_from_upload
|
2018-05-09 12:01:36 +05:30
|
|
|
return unless uploader = build_uploader
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
upload_paths = uploader.upload_paths(params[:filename])
|
2019-09-04 21:01:54 +05:30
|
|
|
upload = Upload.find_by(model: model, uploader: uploader_class.to_s, path: upload_paths)
|
2019-12-21 20:55:43 +05:30
|
|
|
upload&.retrieve_uploader
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2018-12-05 23:21:45 +05:30
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
def build_uploader_from_params
|
2018-05-09 12:01:36 +05:30
|
|
|
return unless uploader = build_uploader
|
|
|
|
|
|
|
|
uploader.retrieve_from_store!(params[:filename])
|
|
|
|
uploader
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_uploader
|
|
|
|
return unless params[:secret] && params[:filename]
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
uploader = uploader_class.new(model, secret: params[:secret])
|
|
|
|
|
2018-05-09 12:01:36 +05:30
|
|
|
return unless uploader.model_valid?
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
uploader
|
|
|
|
end
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
def embeddable?
|
|
|
|
uploader && uploader.exists? && uploader.embeddable?
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
def bypass_auth_checks_on_uploads?
|
2022-08-27 11:52:29 +05:30
|
|
|
if target_project && !target_project.public? && target_project.enforce_auth_checks_on_uploads?
|
|
|
|
return false
|
2022-04-04 11:22:00 +05:30
|
|
|
end
|
2022-07-16 23:28:13 +05:30
|
|
|
|
|
|
|
action_name == 'show' && embeddable?
|
|
|
|
end
|
|
|
|
|
|
|
|
def target_project
|
|
|
|
nil
|
2022-04-04 11:22:00 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def find_model
|
|
|
|
nil
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
def cache_settings
|
|
|
|
[]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def model
|
2023-03-04 22:38:38 +05:30
|
|
|
find_model
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2023-03-04 22:38:38 +05:30
|
|
|
strong_memoize_attr :model
|
2019-09-04 21:01:54 +05:30
|
|
|
|
|
|
|
def workhorse_authorize_request?
|
|
|
|
action_name == 'authorize'
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|