pulp-container 2.26.18__tar.gz → 2.26.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. {pulp_container-2.26.18 → pulp_container-2.26.20}/CHANGES.md +18 -0
  2. {pulp_container-2.26.18 → pulp_container-2.26.20}/PKG-INFO +1 -1
  3. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/__init__.py +1 -1
  4. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/downloaders.py +46 -23
  5. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/models.py +3 -4
  6. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/registry_api.py +1 -1
  7. pulp_container-2.26.20/pulp_container/tests/unit/test_downloaders.py +33 -0
  8. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container.egg-info/PKG-INFO +1 -1
  9. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container.egg-info/SOURCES.txt +1 -0
  10. {pulp_container-2.26.18 → pulp_container-2.26.20}/pyproject.toml +2 -2
  11. {pulp_container-2.26.18 → pulp_container-2.26.20}/COMMITMENT +0 -0
  12. {pulp_container-2.26.18 → pulp_container-2.26.20}/COPYRIGHT +0 -0
  13. {pulp_container-2.26.18 → pulp_container-2.26.20}/LICENSE +0 -0
  14. {pulp_container-2.26.18 → pulp_container-2.26.20}/MANIFEST.in +0 -0
  15. {pulp_container-2.26.18 → pulp_container-2.26.20}/README.rst +0 -0
  16. {pulp_container-2.26.18 → pulp_container-2.26.20}/functest_requirements.txt +0 -0
  17. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/__init__.py +0 -0
  18. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/access_policy.py +0 -0
  19. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/authorization.py +0 -0
  20. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/cache.py +0 -0
  21. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/checks.py +0 -0
  22. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/content.py +0 -0
  23. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/exceptions.py +0 -0
  24. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/fields.py +0 -0
  25. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/global_access_conditions.py +0 -0
  26. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/json_schemas.py +0 -0
  27. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/management/__init__.py +0 -0
  28. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/management/commands/__init__.py +0 -0
  29. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/management/commands/container-handle-image-data.py +0 -0
  30. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/management/commands/container-repair-media-type.py +0 -0
  31. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/management/commands/container-repair-pull-through-distributions.py +0 -0
  32. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0001_initial.py +0 -0
  33. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0001_squashed_0032_upload_artifact.py +0 -0
  34. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0002_containerrepository.py +0 -0
  35. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0003_oci_mediatype.py +0 -0
  36. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0004_upload.py +0 -0
  37. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0005_contentredirectcontentguard.py +0 -0
  38. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0006_containerpushrepository.py +0 -0
  39. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0007_clear_tags_artifacts_refs.py +0 -0
  40. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0008_include_exclude_tags.py +0 -0
  41. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0009_container_namespace.py +0 -0
  42. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0010_remove_uploadchunk.py +0 -0
  43. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0011_add_container_repository_permissions.py +0 -0
  44. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0012_add_container_namespace_permissions.py +0 -0
  45. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0013_add_pull_push_permissions.py +0 -0
  46. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0014_containerdistribution_private.py +0 -0
  47. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0015_manage_tags_push_repo.py +0 -0
  48. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0016_add_delete_versions_permission.py +0 -0
  49. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0017_add_granular_perms.py +0 -0
  50. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0018_containerdistribution_description.py +0 -0
  51. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0019_DATA_distribution_model_swap.py +0 -0
  52. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0020_update_push_repo_perms.py +0 -0
  53. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0021_data_move_redirect_content_guard_to_core.py +0 -0
  54. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0022_delete_contentredirectcontentguard.py +0 -0
  55. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0023_manifestsignature.py +0 -0
  56. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0024_containerremote_sigstore.py +0 -0
  57. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0025_signature_stored_in_textfield.py +0 -0
  58. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0026_manifest_signing_service.py +0 -0
  59. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0027_data_translate_perms_to_roles.py +0 -0
  60. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0028_add_role_manage_permissions.py +0 -0
  61. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0029_remove_blob_media_type.py +0 -0
  62. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0030_enforce_tagged_manifest_reference.py +0 -0
  63. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0031_replace_charf_with_textf.py +0 -0
  64. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0032_upload_artifact.py +0 -0
  65. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0033_raise_warning_for_repair.py +0 -0
  66. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0034_translate_signed_schema.py +0 -0
  67. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0035_alter_blob_content_ptr_and_more.py +0 -0
  68. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0036_containerpushrepository_pending_blobs_manifests.py +0 -0
  69. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0037_create_pull_through_cache_models.py +0 -0
  70. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0038_add_manifest_metadata_fields.py +0 -0
  71. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0039_manifest_data.py +0 -0
  72. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0040_add_remote_repo_filter.py +0 -0
  73. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0041_add_pull_through_pull_permissions.py +0 -0
  74. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0042_add_manifest_nature_field.py +0 -0
  75. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0043_add_os_arch_image_size_manifest_fields.py +0 -0
  76. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0044_add_domain.py +0 -0
  77. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/0045_alter_manifest_compressed_image_size.py +0 -0
  78. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/migrations/__init__.py +0 -0
  79. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/modelresource.py +0 -0
  80. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/pull_through.py +0 -0
  81. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/redirects.py +0 -0
  82. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/registry.py +0 -0
  83. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/replica.py +0 -0
  84. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/serializers.py +0 -0
  85. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/settings.py +0 -0
  86. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/__init__.py +0 -0
  87. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/builder.py +0 -0
  88. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/download_image_data.py +0 -0
  89. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/recursive_add.py +0 -0
  90. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/recursive_remove.py +0 -0
  91. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/sign.py +0 -0
  92. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/sync_stages.py +0 -0
  93. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/synchronize.py +0 -0
  94. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/tag.py +0 -0
  95. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/tasks/untag.py +0 -0
  96. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/token_verification.py +0 -0
  97. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/urls.py +0 -0
  98. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/utils.py +0 -0
  99. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/viewsets.py +0 -0
  100. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/webserver_snippets/__init__.py +0 -0
  101. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/webserver_snippets/apache.conf +0 -0
  102. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/app/webserver_snippets/nginx.conf +0 -0
  103. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/constants.py +0 -0
  104. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/__init__.py +0 -0
  105. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/__init__.py +0 -0
  106. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/__init__.py +0 -0
  107. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_build_images.py +0 -0
  108. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_content_cache.py +0 -0
  109. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_crud_distributions.py +0 -0
  110. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_crud_remotes.py +0 -0
  111. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_domains.py +0 -0
  112. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_flatpak.py +0 -0
  113. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_pull_content.py +0 -0
  114. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_pull_through_cache.py +0 -0
  115. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_pulpimportexport.py +0 -0
  116. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_push_content.py +0 -0
  117. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_push_signatures.py +0 -0
  118. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_rbac_push_repositories.py +0 -0
  119. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_rbac_remotes.py +0 -0
  120. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_rbac_repo_content.py +0 -0
  121. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_rbac_repo_versions.py +0 -0
  122. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_rbac_sync_repositories.py +0 -0
  123. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_recursive_add.py +0 -0
  124. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_recursive_remove.py +0 -0
  125. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_remote_filter_pull_through.py +0 -0
  126. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_repositories_list.py +0 -0
  127. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_sign_manifests.py +0 -0
  128. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_sync.py +0 -0
  129. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_sync_signatures.py +0 -0
  130. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_tagging_images.py +0 -0
  131. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/api/test_token_authentication.py +0 -0
  132. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/conftest.py +0 -0
  133. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/constants.py +0 -0
  134. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/functional/utils.py +0 -0
  135. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/__init__.py +0 -0
  136. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/fixtures/ryuk_0.13.0_manifest_list.json +0 -0
  137. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/test_json_schemas.py +0 -0
  138. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/test_models.py +0 -0
  139. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/test_pull_through_repair.py +0 -0
  140. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/test_serializers.py +0 -0
  141. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container/tests/unit/test_sync_stages.py +0 -0
  142. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container.egg-info/dependency_links.txt +0 -0
  143. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container.egg-info/entry_points.txt +0 -0
  144. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container.egg-info/requires.txt +0 -0
  145. {pulp_container-2.26.18 → pulp_container-2.26.20}/pulp_container.egg-info/top_level.txt +0 -0
  146. {pulp_container-2.26.18 → pulp_container-2.26.20}/setup.cfg +0 -0
  147. {pulp_container-2.26.18 → pulp_container-2.26.20}/test_requirements.txt +0 -0
  148. {pulp_container-2.26.18 → pulp_container-2.26.20}/unittest_requirements.txt +0 -0
@@ -8,6 +8,24 @@
8
8
 
9
9
  [//]: # (towncrier release notes start)
10
10
 
11
+ ## 2.26.20 (2026-10-28) {: #2.26.20 }
12
+
13
+ #### Bugfixes {: #2.26.20-bugfix }
14
+
15
+ - Container blob downloads now respect each Domain's object storage redirect setting.
16
+ - Fixed pull-through signature lookups failing with a 500 when the upstream registry (e.g. gcr.io) sends unquoted values in its bearer auth challenge.
17
+ - Fixed the registry Authorization header leaking into a shared module-level headers dict, which could send one remote's credentials to another registry.
18
+
19
+ ---
20
+
21
+ ## 2.26.19 (2026-10-08) {: #2.26.19 }
22
+
23
+ #### Bugfixes {: #2.26.19-bugfix }
24
+
25
+ - Fixed global auth token leakage. CVE-2026-103868
26
+
27
+ ---
28
+
11
29
  ## 2.26.18 (2026-10-07) {: #2.26.18 }
12
30
 
13
31
  No significant changes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-container
3
- Version: 2.26.18
3
+ Version: 2.26.20
4
4
  Summary: Container plugin for the Pulp Project
5
5
  Author-email: Pulp Team <pulp-list@redhat.com>
6
6
  Project-URL: Homepage, https://pulpproject.org
@@ -24,7 +24,7 @@ class PulpContainerPluginAppConfig(PulpPluginAppConfig):
24
24
 
25
25
  name = "pulp_container.app"
26
26
  label = "container"
27
- version = "2.26.18"
27
+ version = "2.26.20"
28
28
  python_package_name = "pulp-container"
29
29
  domain_compatible = True
30
30
 
@@ -27,15 +27,12 @@ class RegistryAuthHttpDownloader(HttpDownloader):
27
27
  Additionally, use custom headers from DeclarativeArtifact.extra_data['headers']
28
28
  """
29
29
 
30
- registry_auth = {"bearer": None, "basic": None}
31
- token_lock = asyncio.Lock()
32
-
33
30
  def __init__(self, *args, **kwargs):
34
31
  """
35
32
  Initialize the downloader.
36
33
  """
37
34
  self.remote = kwargs.pop("remote")
38
-
35
+ self.factory = kwargs.pop("factory")
39
36
  super().__init__(*args, **kwargs)
40
37
 
41
38
  async def _run(self, handle_401=True, extra_data=None):
@@ -60,11 +57,11 @@ class RegistryAuthHttpDownloader(HttpDownloader):
60
57
  headers = V2_ACCEPT_HEADERS
61
58
  repo_name = None
62
59
  if extra_data is not None:
63
- headers = extra_data.get("headers", headers)
60
+ headers = dict(extra_data.get("headers", headers))
64
61
  repo_name = extra_data.get("repo_name", None)
65
62
  http_method = extra_data.get("http_method", "get") if extra_data is not None else "get"
66
- this_token = self.registry_auth["bearer"]
67
- basic_auth = self.registry_auth["basic"]
63
+ this_token = self.factory.registry_auth["bearer"]
64
+ basic_auth = self.factory.registry_auth["basic"]
68
65
  auth_headers = self.auth_header(this_token, basic_auth)
69
66
  headers.update(auth_headers)
70
67
  # aiohttps does not allow to send auth argument and auth header together
@@ -87,16 +84,16 @@ class RegistryAuthHttpDownloader(HttpDownloader):
87
84
  if "bearer" in response_auth_header.lower():
88
85
  # Token has not been updated during request
89
86
  if (
90
- self.registry_auth["bearer"] is None
91
- or self.registry_auth["bearer"] == this_token
87
+ self.factory.registry_auth["bearer"] is None
88
+ or self.factory.registry_auth["bearer"] == this_token
92
89
  ):
93
- self.registry_auth["bearer"] = None
90
+ self.factory.registry_auth["bearer"] = None
94
91
  await self.update_token(response_auth_header, this_token, repo_name)
95
92
  return await self._run(handle_401=False, extra_data=extra_data)
96
93
  elif "basic" in response_auth_header.lower():
97
94
  if self.remote.username:
98
95
  basic = aiohttp.BasicAuth(self.remote.username, self.remote.password)
99
- self.registry_auth["basic"] = basic.encode()
96
+ self.factory.registry_auth["basic"] = basic.encode()
100
97
  return await self._run(handle_401=False, extra_data=extra_data)
101
98
  else:
102
99
  raise
@@ -117,21 +114,14 @@ class RegistryAuthHttpDownloader(HttpDownloader):
117
114
  """
118
115
  Update the Bearer token to be used with all requests.
119
116
  """
120
- async with self.token_lock:
117
+ async with self.factory.token_lock:
121
118
  if (
122
- self.registry_auth["bearer"] is not None
123
- and self.registry_auth["bearer"] == used_token
119
+ self.factory.registry_auth["bearer"] is not None
120
+ and self.factory.registry_auth["bearer"] == used_token
124
121
  ):
125
122
  return
126
123
  log.info("Updating bearer token")
127
- bearer_info_string = response_auth_header[len("Bearer ") :]
128
- bearer_info_list = re.split(",(?=[^=,]+=)", bearer_info_string)
129
-
130
- # The remaining string consists of comma seperated key=value pairs
131
- auth_query_dict = {}
132
- for key, value in (item.split("=") for item in bearer_info_list):
133
- # The value is a string within a string, ex: '"value"'
134
- auth_query_dict[key] = json.loads(value)
124
+ auth_query_dict = self.parse_auth_challenge(response_auth_header)
135
125
  try:
136
126
  token_base_url = auth_query_dict.pop("realm")
137
127
  except KeyError:
@@ -163,7 +153,25 @@ class RegistryAuthHttpDownloader(HttpDownloader):
163
153
 
164
154
  token_js = json.loads(token_data)
165
155
  token = token_js.get("token") or token_js.get("access_token")
166
- self.registry_auth["bearer"] = token
156
+ self.factory.registry_auth["bearer"] = token
157
+
158
+ @staticmethod
159
+ def parse_auth_challenge(response_auth_header):
160
+ """
161
+ Parse the parameters of a Bearer auth challenge into a dictionary.
162
+
163
+ Values may be quoted strings or plain tokens (RFC 7235), e.g. gcr.io responds with
164
+ 'Bearer realm="https://gcr.io/v2/token",service=gcr.io'.
165
+ """
166
+ bearer_info_string = response_auth_header[len("Bearer ") :]
167
+ auth_query_dict = {}
168
+ for match in re.finditer(r'([^=,\s]+)=(?:"((?:[^"\\]|\\.)*)"|([^,]*))', bearer_info_string):
169
+ key, quoted, plain = match.groups()
170
+ if quoted is not None:
171
+ auth_query_dict[key] = re.sub(r"\\(.)", r"\1", quoted)
172
+ else:
173
+ auth_query_dict[key] = plain.strip()
174
+ return auth_query_dict
167
175
 
168
176
  @staticmethod
169
177
  def auth_header(token, basic_auth):
@@ -193,6 +201,21 @@ class RegistryAuthHttpDownloader(HttpDownloader):
193
201
  )
194
202
 
195
203
 
204
+ class RegistryAuthDownloaderFactory(DownloaderFactory):
205
+ """
206
+ A downloader factory for registry access that can also share the authentication tokens.
207
+ """
208
+
209
+ def __init__(self, *args, **kwargs):
210
+ super().__init__(*args, **kwargs)
211
+ self.registry_auth = {"bearer": None, "basic": None}
212
+ self.token_lock = asyncio.Lock()
213
+
214
+ def _http_or_https(self, download_class, url, **kwargs):
215
+ kwargs.setdefault("factory", self)
216
+ return super()._http_or_https(download_class, url, **kwargs)
217
+
218
+
196
219
  class NoAuthSignatureDownloader(HttpDownloader):
197
220
  """A downloader class suited for signature downloads."""
198
221
 
@@ -12,7 +12,6 @@ from django.shortcuts import redirect
12
12
  from django_lifecycle import AFTER_CREATE, AFTER_DELETE, AFTER_UPDATE, hook
13
13
 
14
14
  from pulpcore.plugin.cache import SyncContentCache
15
- from pulpcore.plugin.download import DownloaderFactory
16
15
  from pulpcore.plugin.models import (
17
16
  Artifact,
18
17
  AutoAddObjPermsMixin,
@@ -498,17 +497,17 @@ class ContainerRemote(Remote, AutoAddObjPermsMixin):
498
497
  """
499
498
  Downloader Factory that maps to custom downloaders which support registry auth.
500
499
 
501
- Upon first access, the DownloaderFactory is instantiated and saved internally.
500
+ Upon first access, the RegistryAuthDownloaderFactory is instantiated and saved internally.
502
501
 
503
502
  Returns:
504
- DownloadFactory: The instantiated DownloaderFactory to be used by
503
+ DownloadFactory: The instantiated RegistryAuthDownloaderFactory to be used by
505
504
  get_downloader()
506
505
 
507
506
  """
508
507
  try:
509
508
  return self._download_factory
510
509
  except AttributeError:
511
- self._download_factory = DownloaderFactory(
510
+ self._download_factory = downloaders.RegistryAuthDownloaderFactory(
512
511
  self,
513
512
  downloader_overrides={
514
513
  "http": downloaders.RegistryAuthHttpDownloader,
@@ -1031,7 +1031,7 @@ class RedirectsMixin:
1031
1031
  domain = get_domain()
1032
1032
  if (
1033
1033
  domain.storage_class == "pulpcore.app.models.storage.FileSystem"
1034
- or not settings.REDIRECT_TO_OBJECT_STORAGE
1034
+ or not domain.redirect_to_object_storage
1035
1035
  ):
1036
1036
  self.redirects_class = FileStorageRedirects
1037
1037
  elif domain.storage_class == "storages.backends.s3boto3.S3Boto3Storage":
@@ -0,0 +1,33 @@
1
+ import pytest
2
+
3
+ from pulp_container.app.downloaders import RegistryAuthHttpDownloader
4
+
5
+
6
+ @pytest.mark.parametrize(
7
+ "header,expected",
8
+ [
9
+ (
10
+ 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io"',
11
+ {"realm": "https://auth.docker.io/token", "service": "registry.docker.io"},
12
+ ),
13
+ (
14
+ 'Bearer realm="https://gcr.io/v2/token",service=gcr.io',
15
+ {"realm": "https://gcr.io/v2/token", "service": "gcr.io"},
16
+ ),
17
+ (
18
+ 'Bearer realm="https://quay.io/v2/auth",service="quay.io",'
19
+ 'scope="repository:foo/bar:pull,push"',
20
+ {
21
+ "realm": "https://quay.io/v2/auth",
22
+ "service": "quay.io",
23
+ "scope": "repository:foo/bar:pull,push",
24
+ },
25
+ ),
26
+ (
27
+ 'Bearer realm="https://example.com/token", service=example.com, error="a \\"b\\""',
28
+ {"realm": "https://example.com/token", "service": "example.com", "error": 'a "b"'},
29
+ ),
30
+ ],
31
+ )
32
+ def test_parse_auth_challenge(header, expected):
33
+ assert RegistryAuthHttpDownloader.parse_auth_challenge(header) == expected
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-container
3
- Version: 2.26.18
3
+ Version: 2.26.20
4
4
  Summary: Container plugin for the Pulp Project
5
5
  Author-email: Pulp Team <pulp-list@redhat.com>
6
6
  Project-URL: Homepage, https://pulpproject.org
@@ -137,6 +137,7 @@ pulp_container/tests/functional/api/test_sync_signatures.py
137
137
  pulp_container/tests/functional/api/test_tagging_images.py
138
138
  pulp_container/tests/functional/api/test_token_authentication.py
139
139
  pulp_container/tests/unit/__init__.py
140
+ pulp_container/tests/unit/test_downloaders.py
140
141
  pulp_container/tests/unit/test_json_schemas.py
141
142
  pulp_container/tests/unit/test_models.py
142
143
  pulp_container/tests/unit/test_pull_through_repair.py
@@ -7,7 +7,7 @@ build-backend = 'setuptools.build_meta'
7
7
 
8
8
  [project]
9
9
  name = "pulp-container"
10
- version = "2.26.18"
10
+ version = "2.26.20"
11
11
  description = "Container plugin for the Pulp Project"
12
12
  readme = "README.md"
13
13
  authors = [
@@ -89,7 +89,7 @@ ignore = [
89
89
  [tool.bumpversion]
90
90
  # This section is managed by the plugin template. Do not edit manually.
91
91
 
92
- current_version = "2.26.18"
92
+ current_version = "2.26.20"
93
93
  commit = false
94
94
  tag = false
95
95
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<alpha>0a)?(?P<patch>\\d+)(\\.(?P<release>[a-z]+))?"