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
|
# 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
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
|