Class: Api::Registry::RegistryProxiesController
- Inherits:
-
V2::ApiController
- Object
- V2::ApiController
- Api::Registry::RegistryProxiesController
- Defined in:
- app/controllers/katello/api/registry/registry_proxies_controller.rb
Overview
rubocop:disable Metrics/ClassLength
Instance Method Summary collapse
- #authenticate_cert_request ⇒ Object
- #authorize_repository_read ⇒ Object
- #authorize_repository_write ⇒ Object
- #catalog ⇒ Object
- #check_blob ⇒ Object
- #check_blob_push_container(props) ⇒ Object
- #check_blob_push_field_syntax(props) ⇒ Object
- #check_blob_push_org_id(props) ⇒ Object
-
#check_blob_push_org_label(props) ⇒ Object
rubocop:disable Metrics/MethodLength.
- #check_blob_push_product_id(props) ⇒ Object
- #check_blob_push_product_label(props) ⇒ Object
- #confirm_settings ⇒ Object
- #container_push_prop_validation(props = nil) ⇒ Object
- #create_container_repo_if_needed ⇒ Object
- #create_manifest ⇒ Object
- #create_tar_file(files, repository, tag) ⇒ Object
- #disable_strong_params ⇒ Object
- #find_readable_repository ⇒ Object
- #find_scope_repository ⇒ Object
- #find_writable_repository ⇒ Object
- #finish_upload_blob ⇒ Object
-
#force_include_layer(repository, digest, layer) ⇒ Object
TODO: Until pulp supports optional upload of layers, include all layers pulp.plan.io/issues/3497.
- #get_manifest_files(repository, manifest) ⇒ Object
- #get_matching_products_from_org(organization, product_label) ⇒ Object
- #get_root_repo_from_product(product, root_repo_name) ⇒ Object
- #header_host_uuid ⇒ Object
- #item_not_found(item) ⇒ Object
- #logger ⇒ Object
- #optional_authorize ⇒ Object
- #parse_blob_push_props(path_string = nil) ⇒ Object
- #ping ⇒ Object
- #process_action(method_name, *args) ⇒ Object
- #pull_blob ⇒ Object
- #pull_manifest ⇒ Object
- #push_manifest ⇒ Object
- #redirect_authorization_headers ⇒ Object
- #redirect_client ⇒ Object
- #registry_authorize ⇒ Object
- #render_podman_error(code, message, status = :bad_request) ⇒ Object
- #repackage_message ⇒ Object
- #request_url ⇒ Object
- #require_user_authorization?(repository = @repository) ⇒ Boolean
- #root_repository ⇒ Object
- #route_name ⇒ Object
- #save_pulp_push_distribution_href(pulp_repo_href) ⇒ Object
- #save_pulp_push_repository_href ⇒ Object
- #save_push_repo_hrefs ⇒ Object
- #set_user_by_token(token, redirect_on_failure = true) ⇒ Object
- #ssl_client_authorized?(org_label) ⇒ Boolean
- #start_upload_blob ⇒ Object
- #static_index ⇒ Object
- #tags_list ⇒ Object
- #tmp_dir ⇒ Object
- #tmp_file(filename) ⇒ Object
- #token ⇒ Object
- #translated_headers_for_proxy ⇒ Object
- #upload_blob ⇒ Object
- #v1_ping ⇒ Object
- #v1_search ⇒ Object
Methods included from Katello::Authentication::ClientAuthentication
#add_candlepin_version_header, #authenticate_client, #cert_from_request, #cert_present?, #set_client_user
Instance Method Details
#authenticate_cert_request ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 47 def authenticate_cert_request if cert_present? client_cert = ::Cert::RhsmClient.new(cert_from_request) uuid = client_cert.uuid auth = authenticate_client if auth ::User.current = ::User.anonymous_admin @host = Katello::Host::ContentFacet.find_by(uuid: uuid)&.host end end end |
#authorize_repository_read ⇒ Object
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 495 def @repository = find_readable_repository return item_not_found(params[:repository]) unless @repository if params[:tag] if params[:tag][0..6] == 'sha256:' manifest = Katello::DockerManifestList.where(digest: params[:tag]).first || Katello::DockerManifest.where(digest: params[:tag]).first return item_not_found(params[:tag]) unless manifest else tag = ::Katello::DockerMetaTag.where(id: ::Katello::RepositoryDockerMetaTag. where(repository_id: @repository.id).select(:docker_meta_tag_id), name: params[:tag]).first return item_not_found(params[:tag]) unless tag end end true end |
#authorize_repository_write ⇒ Object
468 469 470 471 472 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 468 def @repository = find_writable_repository return item_not_found(params[:repository]) unless @repository true end |
#catalog ⇒ Object
703 704 705 706 707 708 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 703 def catalog repositories = Repository.readable_docker_catalog(@host).collect do |repository| repository.container_repository_name end render json: { repositories: repositories } end |
#check_blob ⇒ Object
566 567 568 569 570 571 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 566 def check_blob pulp_response = Resources::Registry::Proxy.get(@_request.fullpath, 'Accept' => request.headers['Accept']) response.headers['Content-Type'] = pulp_response.headers[:content_type] if pulp_response.headers[:content_type] response.headers['Content-Length'] = pulp_response.headers[:content_length] if pulp_response.headers[:content_length] head pulp_response.code end |
#check_blob_push_container(props) ⇒ Object
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 339 def check_blob_push_container(props) unless props[:name].present? && props[:name].length > 0 return render_podman_error( "NAME_INVALID", _("Invalid format. Container name cannot be blank."), :bad_request ) end @container_name = props[:name] @container_push_name_format = props[:schema] if @container_push_name_format == "label" @container_path_input = "#{props[:organization]}/#{props[:product]}/#{props[:name]}" else @container_path_input = "id/#{props[:organization]}/#{props[:product]}/#{props[:name]}" end # If the repo already exists, check if the existing push format matches root_repo = get_root_repo_from_product(@product, @container_name).first if !root_repo.nil? && @container_push_name_format != root_repo.container_push_name_format return render_podman_error( "NAME_INVALID", _("Repository name '%{container_name}' already exists in this product using a different naming scheme. Please retry your request with the %{root_repo_container_push_name} format or destroy and recreate the repository using your preferred schema.") % {container_name: @container_name, root_repo_container_push_name: root_repo.container_push_name_format}, :conflict ) end true end |
#check_blob_push_field_syntax(props) ⇒ Object
171 172 173 174 175 176 177 178 179 180 181 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 171 def check_blob_push_field_syntax(props) # check basic url field syntax unless props[:valid_format] return render_podman_error( "NAME_INVALID", _("Invalid format. Container pushes should follow 'organization_label/product_label/name' OR 'id/organization_id/product_id/name' schema."), :bad_request ) end return true end |
#check_blob_push_org_id(props) ⇒ Object
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 237 def check_blob_push_org_id(props) org_id = props[:organization] unless org_id.present? && org_id == org_id.to_i.to_s return render_podman_error( "NAME_INVALID", _("Invalid format. Organization id must be an integer without leading zeros."), :bad_request ) end @organization = Organization.find_by_id(org_id.to_i) if @organization.nil? return render_podman_error( "NAME_UNKNOWN", _("Organization id not found: '%s'") % org_id, :not_found ) end true end |
#check_blob_push_org_label(props) ⇒ Object
rubocop:disable Metrics/MethodLength
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 184 def check_blob_push_org_label(props) org_label = props[:organization] unless org_label.present? && org_label.length > 0 return render_podman_error( "NAME_INVALID", _("Invalid format. Organization label cannot be blank."), :bad_request ) end org = Organization.where("LOWER(label) = '#{org_label}'") # convert to lowercase # reject ambiguous orgs (possible due to lowercase conversion) if org.length > 1 # Determine if the repo already exists in one of the possible products. If yes, # inform the user they need to destroy the existing repo and use the ID format unless props[:product].blank? || props[:name].blank? org.each do |o| products = get_matching_products_from_org(o, props[:product]) products.each do |prod| root_repos = get_root_repo_from_product(prod, props[:name]) unless root_repos.empty? return render_podman_error( "NAME_INVALID", _("Due to a change in your organizations, this container name has become "\ "ambiguous (org name '%{org_label}'). If you wish to continue using this "\ "container name, destroy the organization in conflict with '%{o_name} (id "\ "%{o_id}). If you wish to keep both orgs, destroy '%{o_label}/%{prod_label}/"\ "%{root_repo_label}' and retry your push using the id format.") % { org_label: org_label, o_name: o.name, o_id: o.id, o_label: o.label, prod_label: prod.label, root_repo_label: root_repos.first.label }, :conflict ) end end end end # Otherwise tell them to try pushing with ID format return render_podman_error( "NAME_INVALID", _("Organization label '%s' is ambiguous. Try using an id-based container name.") % org_label, :conflict ) end if org.length == 0 return render_podman_error( "NAME_UNKNOWN", _("Organization not found: '%s'") % org_label, :not_found ) end @organization = org.first true end |
#check_blob_push_product_id(props) ⇒ Object
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 306 def check_blob_push_product_id(props) prod_id = props[:product] unless prod_id.present? && prod_id == prod_id.to_i.to_s return render_podman_error( "NAME_INVALID", _("Invalid format. Product id must be an integer without leading zeros."), :bad_request ) end @product = @organization.products.find_by_id(prod_id.to_i) if @product.nil? return render_podman_error( "NAME_UNKNOWN", _("Product id not found: '%s'") % prod_id, :not_found ) end true end |
#check_blob_push_product_label(props) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 257 def check_blob_push_product_label(props) prod_label = props[:product] unless prod_label.present? && prod_label.length > 0 return render_podman_error( "NAME_INVALID", _("Invalid format. Product label cannot be blank."), :bad_request ) end product = get_matching_products_from_org(@organization, prod_label) # reject ambiguous products (possible due to lowercase conversion) if product.length > 1 # Determine if the repo already exists in one of the possible products. If yes, # inform the user they need to destroy the existing repo and use the ID format unless props[:name].blank? product.each do |prod| root_repos = get_root_repo_from_product(prod, props[:name]) unless root_repos.empty? return render_podman_error( "NAME_INVALID", _("Due to a change in your products, this container name has become ambiguous "\ "(product name '%{prod_label}'). If you wish to continue using this container "\ "name, destroy the product in conflict with '%{prod_name}' (id %{prod_id}). If "\ "you wish to keep both products, destroy '%{org_label}/%{prod_dot_label}/"\ "%{root_repo_label}' and retry your push using the id format.") % { prod_label: prod_label, prod_name: prod.name, prod_id: prod.id, org_label: @organization.label, prod_dot_label: prod.label, root_repo_label: root_repos.first.label }, :conflict ) end end end return render_podman_error( "NAME_INVALID", _("Product label '%s' is ambiguous. Try using an id-based container name.") % prod_label, :conflict ) end if product.length == 0 return render_podman_error( "NAME_UNKNOWN", _("Product not found: '%s'") % prod_label, :not_found ) end @product = product.first true end |
#confirm_settings ⇒ Object
811 812 813 814 815 816 817 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 811 def confirm_settings if SETTINGS.dig(:katello, :container_image_registry) || SmartProxy.pulp_primary&.pulp3_repository_type_support?(::Katello::Repository::DOCKER_TYPE) return true end render_error('custom_error', :status => :not_found, :locals => { :message => "Registry not configured" }) end |
#container_push_prop_validation(props = nil) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 118 def container_push_prop_validation(props = nil) # Handle validation and repo creation for container pushes before talking to pulp props = parse_blob_push_props if props.nil? return false unless check_blob_push_field_syntax(props) # validate input and find the org and product either using downcase label or id if props[:schema] == "label" return false unless check_blob_push_org_label(props) return false unless check_blob_push_product_label(props) else return false unless check_blob_push_org_id(props) return false unless check_blob_push_product_id(props) end return false unless check_blob_push_container(props) true end |
#create_container_repo_if_needed ⇒ Object
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 370 def create_container_repo_if_needed unless @product.syncable? return render_podman_error( 'DENIED', _("Requested access to '%s' is denied") % @container_name, :not_found ) end if get_root_repo_from_product(@product, @container_name).empty? root = @product.add_repo( name: @container_name, label: @container_name, download_policy: 'immediate', content_type: Repository::DOCKER_TYPE, unprotected: true, is_container_push: true, container_push_name: @container_path_input, container_push_name_format: @container_push_name_format ) sync_task(::Actions::Katello::Repository::CreateContainerPushRoot, root, @container_path_input) end end |
#create_manifest ⇒ Object
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 722 def create_manifest filename = tmp_file('manifest.json') if File.exist? filename render_error('custom_error', :status => :unprocessable_entity, :locals => { :message => "Upload already in progress" }) return nil end manifest = request.body.read File.open(tmp_file('manifest.json'), 'wb', 0600) do |file| file.write manifest end manifest = JSON.parse(manifest) rescue File.delete(tmp_file('manifest.json')) if File.exist? tmp_file('manifest.json') end |
#create_tar_file(files, repository, tag) ⇒ Object
766 767 768 769 770 771 772 773 774 775 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 766 def create_tar_file(files, repository, tag) tar_file = "#{repository}_#{tag}.tar" `/usr/bin/tar cf #{tmp_file(tar_file)} -C #{tmp_dir} #{files.join(' ')}` files.each do |file| filename = tmp_file(file) File.delete(filename) if File.exist? filename end tar_file end |
#disable_strong_params ⇒ Object
807 808 809 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 807 def disable_strong_params params.permit! end |
#find_readable_repository ⇒ Object
474 475 476 477 478 479 480 481 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 474 def find_readable_repository return nil unless params[:repository] repository = Repository.docker_type.find_by(container_repository_name: params[:repository]) if (repository) repository = Repository.readable_docker_catalog(@host).find_by(container_repository_name: params[:repository]) end repository end |
#find_scope_repository ⇒ Object
799 800 801 802 803 804 805 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 799 def find_scope_repository scope = params['scope'] return nil unless scope scopes = scope.split(':') scopes[2] == 'pull' ? Repository.docker_type.non_archived.find_by_container_repository_name(scopes[1]) : nil end |
#find_writable_repository ⇒ Object
464 465 466 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 464 def find_writable_repository Repository.docker_type.syncable.find_by_container_repository_name(params[:repository]) end |
#finish_upload_blob ⇒ Object
635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 635 def finish_upload_blob headers = translated_headers_for_proxy headers['Content-Type'] = request.headers['Content-Type'] if request.headers['Content-Type'] headers['Content-Range'] = request.headers['Content-Range'] if request.headers['Content-Range'] headers['Content-Length'] = request.headers['Content-Length'] if request.headers['Content-Length'] pulp_response = Resources::Registry::Proxy.put(@_request.fullpath, @_request.body, headers) pulp_response.headers.each do |key, value| response.header[key.to_s] = value end save_push_repo_hrefs if pulp_response.code.between?(200, 299) head pulp_response.code end |
#force_include_layer(repository, digest, layer) ⇒ Object
TODO: Until pulp supports optional upload of layers, include all layers pulp.plan.io/issues/3497
787 788 789 790 791 792 793 794 795 796 797 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 787 def force_include_layer(repository, digest, layer) unless File.exist? tmp_file(layer) logger.debug "Getting blob #{digest} to write to #{layer}" fullpath = "/v2/#{repository}/blobs/#{digest}" request = Resources::Registry::Proxy.get(fullpath) File.open(tmp_file(layer), 'wb', 0600) do |file| file.write request.body end logger.debug "Wrote blob #{digest} to #{layer}" end end |
#get_manifest_files(repository, manifest) ⇒ Object
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 738 def get_manifest_files(repository, manifest) files = ['manifest.json'] case manifest['schemaVersion'] when 1 if manifest['fsLayers'] files += manifest['fsLayers'].collect do |layer| layerfile = "#{layer['blobSum'][7..]}.tar" force_include_layer(repository, layer['blobSum'], layerfile) layerfile end end when 2 if manifest['layers'] files += manifest['layers'].collect do |layer| layerfile = "#{layer['digest'][7..]}.tar" force_include_layer(repository, layer['digest'], layerfile) layerfile end end files << "#{manifest['config']['digest'][7..]}.tar" else render_error 'custom_error', :status => :internal_server_error, :locals => { :message => "Unsupported schema #{manifest['schemaVersion']}" } return nil end files end |
#get_matching_products_from_org(organization, product_label) ⇒ Object
326 327 328 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 326 def get_matching_products_from_org(organization, product_label) return organization.products.where("LOWER(label) = '#{product_label}'") # convert to lowercase end |
#get_root_repo_from_product(product, root_repo_name) ⇒ Object
330 331 332 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 330 def get_root_repo_from_product(product, root_repo_name) return product.root_repositories.where(label: root_repo_name) end |
#header_host_uuid ⇒ Object
88 89 90 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 88 def header_host_uuid request.headers['HostUUID'] || request.headers['HTTP_HOSTUUID'] || request.get_header('HTTP_HOSTUUID') end |
#item_not_found(item) ⇒ Object
849 850 851 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 849 def item_not_found(item) render_podman_error("NAME_UNKNOWN", _("%s was not found!") % item, :not_found) end |
#logger ⇒ Object
823 824 825 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 823 def logger ::Foreman::Logging.logger('katello/registry_proxy') end |
#optional_authorize ⇒ Object
92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 92 def @repository = find_scope_repository if @repository && (@repository.environment.registry_unauthenticated_pull || (@repository.organization.label)) true elsif params['action'] == 'catalog' set_user_by_token(request.headers['Authorization'], false) elsif (params['action'] == 'token' && params['scope'].blank? && params['account'].blank?) true else end end |
#parse_blob_push_props(path_string = nil) ⇒ Object
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 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 136 def parse_blob_push_props(path_string = nil) # path string should follow one of these formats: # - /v2/{org_label}/{product_label}/{name}/blobs/uploads... # - /v2/id/{org_id}/{product_id}/{name}/blobs/uploads... # - /v2/{org_label}/{product_label}/{name}/manifests/... # - /v2/id/{org_id}/{product_id}/{name}/manifests/... # inputs not matching format will return {valid_format: false} path_string = @_request.fullpath if path_string.nil? segments = path_string.split('/') if segments.length >= 7 && segments[0] == "" && segments[1] == "v2" && segments[2] != "id" && (segments[5] == "blobs" || segments[5] == "manifests") return { valid_format: true, schema: "label", organization: segments[2], product: segments[3], name: segments[4], } elsif segments.length >= 8 && segments[0] == "" && segments[1] == "v2" && segments[2] == "id" && (segments[6] == "blobs" || segments[6] == "manifests") return { valid_format: true, schema: "id", organization: segments[3], product: segments[4], name: segments[5], } else return {valid_format: false} end end |
#ping ⇒ Object
665 666 667 668 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 665 def ping response.headers['Docker-Distribution-API-Version'] = 'registry/2.0' render json: {}, status: :ok end |
#process_action(method_name, *args) ⇒ Object
833 834 835 836 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 833 def process_action(method_name, *args) ::Api::V2::BaseController.instance_method(:process_action).bind(self).call(method_name, *args) Rails.logger.debug "With body: #{filter_sensitive_data(response.body)}\n" unless route_name == 'pull_blob' end |
#pull_blob ⇒ Object
588 589 590 591 592 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 588 def pull_blob headers = {} headers['Accept'] = request.headers['Accept'] if request.headers['Accept'] redirect_client { Resources::Registry::Proxy.get(@_request.fullpath, headers, max_redirects: 0) } end |
#pull_manifest ⇒ Object
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 549 def pull_manifest headers = {} env = request.env.select do |key, _value| key.match("^HTTP_.*") end env.each do |header| headers[header[0].split('_')[1..].join('-')] = header[1] end if (manifest_response = redirect_client { Resources::Registry::Proxy.get(@_request.fullpath, headers) }) response.header['Docker-Content-Digest'] = manifest_response.headers[:docker_content_digest] response.headers['Content-Type'] = manifest_response.headers[:content_type] response.header['Content-Length'] = manifest_response.headers[:content_length] render json: manifest_response end end |
#push_manifest ⇒ Object
650 651 652 653 654 655 656 657 658 659 660 661 662 663 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 650 def push_manifest headers = translated_headers_for_proxy headers['Content-Type'] = request.headers['Content-Type'] if request.headers['Content-Type'] body = @_request.body.read pulp_response = Resources::Registry::Proxy.put(@_request.fullpath, body, headers) pulp_response.headers.each do |key, value| response.header[key.to_s] = value end save_push_repo_hrefs if pulp_response.code.between?(200, 299) # Indexing content is only needed after pushing manifests root_repository.library_instance.index_content head pulp_response.code end |
#redirect_authorization_headers ⇒ Object
59 60 61 62 63 64 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 59 def response.headers['Docker-Distribution-API-Version'] = 'registry/2.0' response.headers['Www-Authenticate'] = "Bearer realm=\"#{request_url}/v2/token\"," \ "service=\"#{request.host}\"," \ "scope=\"repository:registry:pull,push\"" end |
#redirect_client ⇒ Object
573 574 575 576 577 578 579 580 581 582 583 584 585 586 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 573 def redirect_client return yield rescue RestClient::Exception => exception if [301, 302, 307].include?(exception.response.code) # Handle aliased hostname on original request redirect_location = URI(exception.response.headers[:location]) redirect_location.host = request.host if request.host.present? redirect_to redirect_location.to_s, allow_other_host: true nil else raise exception end end |
#registry_authorize ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 105 def @repository = find_readable_repository return true if ['GET', 'HEAD'].include?(request.method) && @repository && ! is_user_set = set_user_by_token(request.headers['Authorization']) return true if is_user_set render_error('unauthorized', :status => :unauthorized) return false end |
#render_podman_error(code, message, status = :bad_request) ⇒ Object
838 839 840 841 842 843 844 845 846 847 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 838 def render_podman_error(code, , status = :bad_request) # Renders a podman-compatible error and returns false. # code: uppercase string code from opencontainer error code spec: # https://specs.opencontainers.org/distribution-spec/?v=v1.0.0#DISTRIBUTION-SPEC-140 # message: a custom error string # status: a symbol in the 400 block of the rails response code table: # https://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option render json: {errors: [{code: code, message: }]}, status: status false end |
#repackage_message ⇒ Object
20 21 22 23 24 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 20 def yield ensure response.headers['Docker-Distribution-API-Version'] = 'registry/2.0' end |
#request_url ⇒ Object
819 820 821 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 819 def request_url request.protocol + request.host_with_port end |
#require_user_authorization?(repository = @repository) ⇒ Boolean
483 484 485 486 487 488 489 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 483 def (repository = @repository) !(params['action'] == 'token' && params['scope'].blank? && params['account'].blank?) && (!repository || (!repository.archive? && !repository.environment.registry_unauthenticated_pull && !(repository.organization.label))) end |
#root_repository ⇒ Object
334 335 336 337 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 334 def root_repository @root_repository ||= get_root_repo_from_product(@product, @container_name)&.first @root_repository end |
#route_name ⇒ Object
827 828 829 830 831 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 827 def route_name Engine.routes.router.recognize(request) do |_, params| break params[:action] if params[:action] end end |
#save_pulp_push_distribution_href(pulp_repo_href) ⇒ Object
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 429 def save_pulp_push_distribution_href(pulp_repo_href) instance_repo = root_repository&.library_instance pulp_api = instance_repo.backend_service(SmartProxy.pulp_primary).api instance_repo = root_repository&.library_instance distribution_api_response = pulp_api.container_push_distribution_for_repository(pulp_repo_href) pulp_distribution_href = distribution_api_response&.pulp_href if pulp_distribution_href.empty? return render_podman_error( "BLOB_UPLOAD_UNKNOWN", _("Could not locate Pulp distribution."), :not_found ) end dist = ::Katello::Pulp3::DistributionReference.where(path: @container_path_input, href: pulp_distribution_href, repository_id: instance_repo.id).first if dist if dist.href != pulp_distribution_href dist.update(href: pulp_distribution_href) end else ::Katello::Pulp3::DistributionReference.create!(path: @container_path_input, href: pulp_distribution_href, repository_id: instance_repo.id) end end |
#save_pulp_push_repository_href ⇒ Object
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 394 def save_pulp_push_repository_href instance_repo = root_repository&.library_instance unless root_repository.present? && instance_repo.present? return render_podman_error( "BLOB_UPLOAD_UNKNOWN", _("Could not locate local uploaded repository for content indexing."), :not_found ) end pulp_api = instance_repo.backend_service(SmartProxy.pulp_primary).api push_repo_api_response = pulp_api.container_push_repo_for_name(@container_path_input) latest_version_href = push_repo_api_response&.latest_version_href pulp_repo_href = push_repo_api_response&.pulp_href if latest_version_href.empty? || pulp_repo_href.empty? return render_podman_error( "BLOB_UPLOAD_UNKNOWN", _("Could not locate repository properties for content indexing."), :not_found ) end instance_repo.update!(version_href: latest_version_href) # The Pulp repository should not change after first creation if root_repository.repository_references.empty? ::Katello::Pulp3::RepositoryReference.where(root_repository_id: instance_repo.root_id, content_view_id: instance_repo.content_view.id, repository_href: pulp_repo_href).create! end return pulp_repo_href end |
#save_push_repo_hrefs ⇒ Object
457 458 459 460 461 462 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 457 def save_push_repo_hrefs # After content upload, save Pulp hrefs. pulp_repo_href = save_pulp_push_repository_href return unless pulp_repo_href save_pulp_push_distribution_href(pulp_repo_href) end |
#set_user_by_token(token, redirect_on_failure = true) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 66 def set_user_by_token(token, redirect_on_failure = true) if token token_type, token = token.split if token_type == 'Bearer' && token personal_token = PersonalAccessToken.find_by_token(token) if personal_token && !personal_token.expired? User.current = User.unscoped.find(personal_token.user_id) return true if User.current end elsif token_type == 'Basic' && token return true if if redirect_on_failure return false end elsif header_host_uuid @host = Katello::Host::ContentFacet.find_by(uuid: header_host_uuid)&.host else authenticate_cert_request end return @host.presence end |
#ssl_client_authorized?(org_label) ⇒ Boolean
491 492 493 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 491 def (org_label) request.headers['HTTP_SSL_CLIENT_VERIFY'] == "SUCCESS" && request.headers['HTTP_SSL_CLIENT_S_DN'] == "O=#{org_label}" end |
#start_upload_blob ⇒ Object
605 606 607 608 609 610 611 612 613 614 615 616 617 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 605 def start_upload_blob headers = translated_headers_for_proxy headers['Content-Type'] = request.headers['Content-Type'] if request.headers['Content-Type'] headers['Content-Length'] = request.headers['Content-Length'] if request.headers['Content-Length'] pulp_response = Resources::Registry::Proxy.post(@_request.fullpath, @_request.body, headers) pulp_response.headers.each do |key, value| response.header[key.to_s] = value end save_push_repo_hrefs if pulp_response.code.between?(200, 299) head pulp_response.code end |
#static_index ⇒ Object
853 854 855 856 857 858 859 860 861 862 863 864 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 853 def static_index host_ip = request.remote_ip @host ||= ::Host.joins(:primary_interface).where("nics.ip = :host_ip OR nics.ip6 = :host_ip", host_ip: host_ip)&.first flatpak_index = (redirect_client { Resources::Registry::Proxy.get(@_request.fullpath, headers) }) flatpak_index_json = JSON.parse(flatpak_index) repos = Repository.readable_docker_catalog(@host) available_container_repo_names = repos.map(&:container_repository_name) flatpak_index_json['Results'] = flatpak_index_json['Results'].select do |result| available_container_repo_names.include?(result['Name']) end render json: flatpak_index_json end |
#tags_list ⇒ Object
710 711 712 713 714 715 716 717 718 719 720 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 710 def = @repository..collect do |tag| tag.name end .uniq! .sort! render json: { name: @repository.container_repository_name, tags: , } end |
#tmp_dir ⇒ Object
777 778 779 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 777 def tmp_dir "#{Rails.root}/tmp" end |
#tmp_file(filename) ⇒ Object
781 782 783 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 781 def tmp_file(filename) File.join(tmp_dir, filename) end |
#token ⇒ Object
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 513 def token if ! personal_token = OpenStruct.new(token: 'unauthenticated', issued_at: Time.now, expires_at: 3.minutes.from_now) else PersonalAccessToken.transaction do personal_token = PersonalAccessToken.where(user_id: User.current.id, name: 'registry').first if personal_token.nil? personal_token = PersonalAccessToken.new(user: User.current, name: 'registry', expires_at: Setting['registry_token_expiration_minutes'].minutes.from_now) personal_token.generate_token personal_token.save! else personal_token.expires_at = Setting['registry_token_expiration_minutes'].minutes.from_now personal_token.save! end rescue ActiveRecord::RecordInvalid personal_token = PersonalAccessToken.where(user_id: User.current.id, name: 'registry').reload.first end end create_time = (personal_token.created_at || personal_token.issued_at).to_time expiry_time = personal_token.expires_at.to_time expiration_seconds = (expiry_time - create_time).to_int # result already in seconds response.headers['Docker-Distribution-API-Version'] = 'registry/2.0' render json: { token: personal_token.token, expires_in: expiration_seconds, issued_at: create_time.rfc3339, # We're keeping the 'expires_at' field for now to maintain compatibility with existing # smart-proxies during 4.11 upgrades. This is not a part of OAuth2 spec. # TODO - Remove 'expires_at' in Katello 4.13 or later. expires_at: expiry_time.rfc3339, } end |
#translated_headers_for_proxy ⇒ Object
594 595 596 597 598 599 600 601 602 603 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 594 def translated_headers_for_proxy current_headers = {} env = request.env.select do |key, _value| key.match("^HTTP_.*") end env.each do |header| current_headers[header[0].split('_')[1..].join('-')] = header[1] end current_headers end |
#upload_blob ⇒ Object
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 619 def upload_blob headers = translated_headers_for_proxy headers['Content-Type'] = request.headers['Content-Type'] if request.headers['Content-Type'] headers['Content-Range'] = request.headers['Content-Range'] if request.headers['Content-Range'] headers['Content-Length'] = request.headers['Content-Length'] if request.headers['Content-Length'] body = @_request.body.read pulp_response = Resources::Registry::Proxy.patch(@_request.fullpath, body, headers) pulp_response.headers.each do |key, value| response.header[key.to_s] = value end save_push_repo_hrefs if pulp_response.code.between?(200, 299) head pulp_response.code end |
#v1_ping ⇒ Object
670 671 672 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 670 def v1_ping head :ok end |
#v1_search ⇒ Object
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 |
# File 'app/controllers/katello/api/registry/registry_proxies_controller.rb', line 674 def v1_search # Checks for v2 client and issues a 404 in that case. Podman # examines the response from a /v1_search request. If the result # is a 4XX, it will then proceed with a request to /_catalog if request.headers['HTTP_DOCKER_DISTRIBUTION_API_VERSION'] == 'registry/2.0' render json: {}, status: :not_found return end authenticate # to set current_user, not to enforce = { resource_class: Katello::Repository, } params[:per_page] = params[:n] || 25 params[:search] = params[:q] search_results = scoped_search(Repository.readable_docker_catalog(@host).distinct, :container_repository_name, :asc, ) results = { num_results: search_results[:subtotal], query: params[:search], } results[:results] = search_results[:results].collect do |repository| { name: repository[:container_repository_name], description: repository[:description] } end render json: results, status: :ok end |