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 178 179 180 181 182 183 184 185 186
|
# frozen_string_literal: true
class Groups::DependencyProxyForContainersController < ::Groups::DependencyProxy::ApplicationController
include DependencyProxy::GroupAccess
include SendFileUpload
include ::PackagesHelper # for event tracking
include WorkhorseRequest
include Gitlab::Utils::StrongMemoize
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 :virtual_registry
urgency :low
PERMITTED_PARAMS = [:image, :tag, :file, :sha, :group_id].freeze
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, permitted_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: permitted_params[:file],
size: permitted_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: permitted_params[:file],
size: permitted_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 group
Group.find_by_full_path(permitted_params[:group_id], follow_redirects: true)
end
strong_memoize_attr :group
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 ||= "#{permitted_params[:sha].sub('sha256:', '')}.gz"
end
def manifest_file_name
@manifest_file_name ||= Gitlab::PathTraversal.check_path_traversal!("#{image}:#{tag}.json")
end
def image
permitted_params[:image]
end
def tag
permitted_params[:tag]
end
def permitted_params
params.permit(PERMITTED_PARAMS)
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: ::DependencyProxy::Manifest::ACCEPTED_TYPES)
end
end
|