1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
# frozen_string_literal: true
module UploadsActions
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
include SendFileUpload
# Starting with version 2, Markdown upload URLs use project / group IDs instead of paths
ID_BASED_UPLOAD_PATH_VERSION = 2
UPLOAD_MOUNTS = %w[avatar attachment file logo pwa_icon header_logo favicon screenshot].freeze
included do
prepend_before_action :set_request_format_from_path_extension
rescue_from FileUploader::InvalidSecret, with: :render_404
rescue_from ::Gitlab::PathTraversal::PathTraversalAttackError do
head :bad_request
end
end
def create
uploader = UploadService.new(model, params[:file], uploader_class, uploaded_by_user_id: current_user&.id).execute
respond_to do |format|
if uploader
format.json do
render json: { link: uploader.to_h }
end
else
format.json do
render json: _('Invalid file.'), status: :unprocessable_entity
end
end
end
end
# This should either
# - send the file directly
# - or redirect to its URL
#
def show
Gitlab::PathTraversal.check_path_traversal!(params[:filename])
return render_404 unless uploader&.exists?
ttl, directives = *cache_settings
ttl ||= 0
directives ||= { private: true, must_revalidate: true }
expires_in ttl, directives
file_uploader = [uploader, *uploader.versions.values].find do |version|
version.filename == params[:filename]
end
return render_404 unless file_uploader
workhorse_set_content_type!
send_upload(file_uploader, attachment: file_uploader.filename, disposition: content_disposition)
end
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
rescue SocketError
render json: _("Error uploading file"), status: :internal_server_error
end
private
# 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']
return unless match = path&.match(/\.(\w+)\z/)
format = Mime[match.captures.first]
request.format = format.symbol if format
end
def content_disposition
if uploader.embeddable? || uploader.pdf?
'inline'
else
'attachment'
end
end
def uploader_class
raise NotImplementedError
end
def upload_mount
mounted_as = params[:mounted_as]
mounted_as if UPLOAD_MOUNTS.include?(mounted_as)
end
def uploader_mounted?
upload_model_class < CarrierWave::Mount::Extension && !upload_mount.nil?
end
def uploader
if uploader_mounted?
model.public_send(upload_mount) # rubocop:disable GitlabSecurity/PublicSend
else
build_uploader_from_upload
end
end
strong_memoize_attr :uploader
# rubocop: disable CodeReuse/ActiveRecord
def build_uploader_from_upload
return unless uploader = build_uploader
upload_paths = uploader.upload_paths(params[:filename])
upload = Upload.find_by(model: model, uploader: uploader_class.to_s, path: upload_paths)
upload&.retrieve_uploader
end
# rubocop: enable CodeReuse/ActiveRecord
def build_uploader
return unless params[:secret] && params[:filename]
uploader = uploader_class.new(model, secret: params[:secret])
return unless uploader.model_valid?
uploader
end
def embeddable?
uploader && uploader.exists? && uploader.embeddable?
end
def bypass_auth_checks_on_uploads?
return false if target_project && !target_project.public? && target_project.enforce_auth_checks_on_uploads?
action_name == 'show' && embeddable?
end
def upload_version_at_least?(version)
return unless uploader && uploader.upload
uploader.upload.version >= version
end
def target_project
nil
end
def find_model
nil
end
def cache_settings
[]
end
def model
find_model
end
strong_memoize_attr :model
def workhorse_authorize_request?
action_name == 'authorize'
end
end
|