178 lines
5.1 KiB
Ruby
178 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Groups::DependencyProxyForContainersController < ::Groups::DependencyProxy::ApplicationController
|
|
include Gitlab::Utils::StrongMemoize
|
|
include DependencyProxy::GroupAccess
|
|
include SendFileUpload
|
|
include ::PackagesHelper # for event tracking
|
|
include WorkhorseRequest
|
|
|
|
before_action :ensure_group
|
|
before_action :ensure_token_granted!, only: [:blob, :manifest]
|
|
before_action :ensure_feature_enabled!
|
|
|
|
before_action :verify_workhorse_api!, only: [:authorize_upload_blob, :upload_blob, :authorize_upload_manifest, :upload_manifest]
|
|
skip_before_action :verify_authenticity_token, only: [:authorize_upload_blob, :upload_blob, :authorize_upload_manifest, :upload_manifest]
|
|
|
|
attr_reader :token
|
|
|
|
feature_category :dependency_proxy
|
|
urgency :low
|
|
|
|
def manifest
|
|
result = DependencyProxy::FindCachedManifestService.new(group, image, tag, token).execute
|
|
|
|
if result[:status] == :success
|
|
if result[:manifest]
|
|
send_manifest(result[:manifest], from_cache: result[:from_cache])
|
|
else
|
|
send_dependency(manifest_header, DependencyProxy::Registry.manifest_url(image, tag), manifest_file_name)
|
|
end
|
|
else
|
|
render status: result[:http_status], json: result[:message]
|
|
end
|
|
end
|
|
|
|
def blob
|
|
blob = @group.dependency_proxy_blobs.find_by_file_name(blob_file_name)
|
|
|
|
if blob.present?
|
|
event_name = tracking_event_name(object_type: :blob, from_cache: true)
|
|
track_package_event(event_name, :dependency_proxy, namespace: group, user: auth_user)
|
|
|
|
send_upload(blob.file)
|
|
else
|
|
send_dependency(token_header, DependencyProxy::Registry.blob_url(image, params[:sha]), blob_file_name)
|
|
end
|
|
end
|
|
|
|
def authorize_upload_blob
|
|
set_workhorse_internal_api_content_type
|
|
|
|
render json: DependencyProxy::FileUploader.workhorse_authorize(has_length: false, maximum_size: DependencyProxy::Blob::MAX_FILE_SIZE)
|
|
end
|
|
|
|
def upload_blob
|
|
@group.dependency_proxy_blobs.create!(
|
|
file_name: blob_file_name,
|
|
file: params[:file],
|
|
size: params[:file].size
|
|
)
|
|
|
|
event_name = tracking_event_name(object_type: :blob, from_cache: false)
|
|
track_package_event(event_name, :dependency_proxy, namespace: group, user: auth_user)
|
|
|
|
head :ok
|
|
end
|
|
|
|
def authorize_upload_manifest
|
|
set_workhorse_internal_api_content_type
|
|
|
|
render json: DependencyProxy::FileUploader.workhorse_authorize(has_length: false, maximum_size: DependencyProxy::Manifest::MAX_FILE_SIZE)
|
|
end
|
|
|
|
def upload_manifest
|
|
attrs = {
|
|
file_name: manifest_file_name,
|
|
content_type: request.headers[Gitlab::Workhorse::SEND_DEPENDENCY_CONTENT_TYPE_HEADER],
|
|
digest: request.headers[DependencyProxy::Manifest::DIGEST_HEADER],
|
|
file: params[:file],
|
|
size: params[:file].size
|
|
}
|
|
|
|
manifest = @group.dependency_proxy_manifests
|
|
.active
|
|
.find_by_file_name(manifest_file_name)
|
|
|
|
if manifest
|
|
manifest.update!(attrs)
|
|
else
|
|
@group.dependency_proxy_manifests.create!(attrs)
|
|
end
|
|
|
|
event_name = tracking_event_name(object_type: :manifest, from_cache: false)
|
|
track_package_event(event_name, :dependency_proxy, namespace: group, user: auth_user)
|
|
|
|
head :ok
|
|
end
|
|
|
|
private
|
|
|
|
def send_manifest(manifest, from_cache:)
|
|
response.headers[DependencyProxy::Manifest::DIGEST_HEADER] = manifest.digest
|
|
response.headers['Content-Length'] = manifest.size
|
|
response.headers['Docker-Distribution-Api-Version'] = DependencyProxy::DISTRIBUTION_API_VERSION
|
|
response.headers['Etag'] = "\"#{manifest.digest}\""
|
|
content_type = manifest.content_type
|
|
|
|
event_name = tracking_event_name(object_type: :manifest, from_cache: from_cache)
|
|
track_package_event(event_name, :dependency_proxy, namespace: group, user: auth_user)
|
|
|
|
send_upload(
|
|
manifest.file,
|
|
proxy: true,
|
|
redirect_params: { query: { 'response-content-type' => content_type } },
|
|
send_params: { type: content_type }
|
|
)
|
|
end
|
|
|
|
def blob_file_name
|
|
@blob_file_name ||= params[:sha].sub('sha256:', '') + '.gz'
|
|
end
|
|
|
|
def manifest_file_name
|
|
@manifest_file_name ||= Gitlab::Utils.check_path_traversal!("#{image}:#{tag}.json")
|
|
end
|
|
|
|
def group
|
|
strong_memoize(:group) do
|
|
Group.find_by_full_path(params[:group_id], follow_redirects: true)
|
|
end
|
|
end
|
|
|
|
def image
|
|
params[:image]
|
|
end
|
|
|
|
def tag
|
|
params[:tag]
|
|
end
|
|
|
|
def tracking_event_name(object_type:, from_cache:)
|
|
event_name = "pull_#{object_type}"
|
|
event_name = "#{event_name}_from_cache" if from_cache
|
|
|
|
event_name
|
|
end
|
|
|
|
def dependency_proxy
|
|
@dependency_proxy ||= group.dependency_proxy_setting
|
|
end
|
|
|
|
def ensure_group
|
|
render_404 unless group
|
|
end
|
|
|
|
def ensure_feature_enabled!
|
|
render_404 unless dependency_proxy.enabled
|
|
end
|
|
|
|
def ensure_token_granted!
|
|
result = DependencyProxy::RequestTokenService.new(image).execute
|
|
|
|
if result[:status] == :success
|
|
@token = result[:token]
|
|
else
|
|
render status: result[:http_status], json: result[:message]
|
|
end
|
|
end
|
|
|
|
def token_header
|
|
{ Authorization: ["Bearer #{token}"] }
|
|
end
|
|
|
|
def manifest_header
|
|
token_header.merge(Accept: ::ContainerRegistry::Client::ACCEPTED_TYPES)
|
|
end
|
|
end
|