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
|
# frozen_string_literal: true
module API
module Concerns
module VirtualRegistries
module Packages
module Endpoint
extend ActiveSupport::Concern
NO_BROWSER_EXECUTION_RESPONSE_HEADERS = { 'Content-Security-Policy' => "default-src 'none'" }.freeze
MAJOR_BROWSERS = %i[webkit firefox ie edge opera chrome].freeze
WEB_BROWSER_ERROR_MESSAGE = 'This endpoint is not meant to be accessed by a web browser.'
UPSTREAM_GID_HEADER = 'X-Gitlab-Virtual-Registry-Upstream-Global-Id'
MAX_FILE_SIZE = 5.gigabytes
included do
helpers do
def require_non_web_browser!
browser = ::Browser.new(request.user_agent)
bad_request!(WEB_BROWSER_ERROR_MESSAGE) if MAJOR_BROWSERS.any? { |b| browser.method(:"#{b}?").call }
end
def send_successful_response_from(service_response:)
action, action_params = service_response.to_h.values_at(:action, :action_params)
case action
when :workhorse_upload_url
workhorse_upload_url(**action_params.slice(:url, :upstream))
when :download_file
present_carrierwave_file!(
action_params[:file],
content_type: action_params[:content_type],
content_disposition: 'inline'
)
when :download_digest
content_type 'text/plain'
env['api.format'] = :binary # to return data as-is
body action_params[:digest]
end
end
def send_error_response_from!(service_response:)
case service_response.reason
when :unauthorized
unauthorized!
when :file_not_found_on_upstreams, :digest_not_found_in_cached_responses
not_found!(service_response.message)
else
bad_request!(service_response.message)
end
end
def workhorse_upload_url(url:, upstream:)
allow_localhost = Gitlab.dev_or_test_env? ||
Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
allowed_uris = ObjectStoreSettings.enabled_endpoint_uris
send_workhorse_headers(
Gitlab::Workhorse.send_dependency(
upstream.headers,
url,
response_headers: NO_BROWSER_EXECUTION_RESPONSE_HEADERS,
allow_localhost: allow_localhost,
allowed_uris: allowed_uris,
ssrf_filter: true,
upload_config: {
headers: { UPSTREAM_GID_HEADER => upstream.to_global_id.to_s },
authorized_upload_response: authorized_upload_response
}
)
)
end
def authorized_upload_response
::VirtualRegistries::CachedResponseUploader.workhorse_authorize(
has_length: true,
maximum_size: MAX_FILE_SIZE,
use_final_store_path: true,
final_store_path_root_id: registry.id
)
end
def send_workhorse_headers(headers)
header(*headers)
env['api.format'] = :binary
content_type 'application/octet-stream'
status :ok
body ''
end
def ok_empty_response
status :ok
env['api.format'] = :binary # to return data as-is
body ''
end
end
after_validation do
require_non_web_browser!
end
end
end
end
end
end
end
|