katello 3.7.1.1 → 3.8.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -7
  3. data/app/assets/javascripts/katello/common/index.js +0 -1
  4. data/app/assets/javascripts/katello/sync_management/index.js +0 -1
  5. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +477 -0
  6. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -1
  7. data/app/controllers/katello/api/v2/activation_keys_controller.rb +2 -2
  8. data/app/controllers/katello/api/v2/api_controller.rb +3 -1
  9. data/app/controllers/katello/api/v2/content_credentials_controller.rb +1 -1
  10. data/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb +1 -1
  11. data/app/controllers/katello/api/v2/content_view_versions_controller.rb +15 -1
  12. data/app/controllers/katello/api/v2/environments_controller.rb +5 -2
  13. data/app/controllers/katello/api/v2/errata_controller.rb +48 -34
  14. data/app/controllers/katello/api/v2/gpg_keys_controller.rb +1 -1
  15. data/app/controllers/katello/api/v2/host_collections_controller.rb +1 -1
  16. data/app/controllers/katello/api/v2/host_packages_controller.rb +5 -1
  17. data/app/controllers/katello/api/v2/organizations_controller.rb +1 -1
  18. data/app/controllers/katello/api/v2/packages_controller.rb +33 -22
  19. data/app/controllers/katello/api/v2/products_controller.rb +1 -1
  20. data/app/controllers/katello/api/v2/repositories_controller.rb +6 -5
  21. data/app/controllers/katello/api/v2/repository_sets_controller.rb +1 -10
  22. data/app/controllers/katello/api/v2/sync_plans_controller.rb +1 -1
  23. data/app/controllers/katello/concerns/api/v2/repository_content_controller.rb +20 -12
  24. data/app/controllers/katello/remote_execution_controller.rb +6 -6
  25. data/app/helpers/katello/hosts_and_hostgroups_helper.rb +9 -37
  26. data/app/lib/actions/katello/content_view/promote.rb +6 -2
  27. data/app/lib/actions/katello/content_view_version/after_promote_hook.rb +11 -0
  28. data/app/lib/actions/katello/content_view_version/before_promote_hook.rb +11 -0
  29. data/app/lib/actions/katello/host/remove_subscriptions.rb +1 -1
  30. data/app/lib/actions/pulp/consumer/abstract_content_action.rb +0 -12
  31. data/app/lib/actions/pulp/consumer/content_install.rb +1 -1
  32. data/app/lib/actions/pulp/consumer/content_uninstall.rb +1 -1
  33. data/app/lib/actions/pulp/consumer/content_update.rb +1 -1
  34. data/app/lib/katello/resources/registry.rb +40 -0
  35. data/app/lib/katello/util/package.rb +9 -4
  36. data/app/models/katello/concerns/subscription_facet_host_extensions.rb +1 -1
  37. data/app/models/katello/content_view.rb +4 -12
  38. data/app/models/katello/content_view_version.rb +26 -0
  39. data/app/models/katello/glue/candlepin/owner.rb +8 -0
  40. data/app/models/katello/glue/candlepin/pool.rb +11 -11
  41. data/app/models/katello/glue/candlepin/repository.rb +1 -1
  42. data/app/models/katello/glue/pulp/repos.rb +1 -0
  43. data/app/models/katello/host/content_facet.rb +1 -2
  44. data/app/models/katello/kt_environment.rb +6 -0
  45. data/app/models/katello/product_content.rb +1 -4
  46. data/app/models/katello/repository.rb +2 -0
  47. data/app/models/katello/rpm.rb +118 -14
  48. data/app/services/katello/puppet_class_importer_extensions.rb +17 -20
  49. data/app/services/katello/ui_notifications/pulp/proxy_disk_space.rb +1 -3
  50. data/app/views/katello/api/v2/common/copy.json.rabl +3 -0
  51. data/app/views/katello/api/v2/environments/show.json.rabl +1 -1
  52. data/app/views/katello/api/v2/repositories/base.json.rabl +1 -0
  53. data/app/views/katello/api/v2/repositories/show.json.rabl +4 -2
  54. data/app/views/overrides/activation_keys/_host_environment_select.html.erb +3 -2
  55. data/config/katello.yaml +89 -0
  56. data/config/routes.rb +0 -1
  57. data/config/routes/api/registry.rb +29 -0
  58. data/config/routes/api/v2.rb +1 -1
  59. data/db/migrate/20180612163403_add_foreign_key_to_hypervisor_id.rb +0 -3
  60. data/db/migrate/20180614184822_add_unauthenticated_pull.rb +9 -0
  61. data/db/migrate/20180618195941_add_description_to_repository.rb +5 -0
  62. data/db/seeds.d/75-job_templates.rb +2 -5
  63. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-repository-sets-modal.controller.js +3 -4
  64. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-subscriptions-modal.controller.js +1 -4
  65. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-subscriptions-modal.html +1 -1
  66. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content-host-register.controller.js +1 -1
  67. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/content-host-packages-installed.controller.js +1 -1
  68. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-add-subscriptions.controller.js +1 -1
  69. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/content-views.routes.js +1 -0
  70. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/content-view-version.controller.js +32 -18
  71. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/views/content-view-version-details.html +1 -1
  72. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/views/content-view-version-docker.html +2 -0
  73. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-details.html +32 -23
  74. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +6 -0
  75. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +8 -0
  76. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/discovery.controller.js +1 -1
  77. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery-create.html +1 -1
  78. data/engines/bastion_katello/app/assets/stylesheets/bastion_katello/bastion_katello.scss +0 -5
  79. data/lib/katello/engine.rb +1 -0
  80. data/lib/katello/permission_creator.rb +2 -0
  81. data/lib/katello/permissions/registry_permissions.rb +20 -0
  82. data/lib/katello/plugin.rb +1 -0
  83. data/lib/katello/tasks/clean_backend_objects.rake +3 -12
  84. data/lib/katello/version.rb +1 -1
  85. data/package.json +7 -10
  86. data/webpack/components/Search/Search.test.js +1 -3
  87. data/webpack/containers/Application/config.js +2 -9
  88. data/webpack/containers/Application/index.js +2 -4
  89. data/webpack/mockRequest.js +3 -3
  90. data/webpack/move_to_foreman/common/helpers.js +8 -45
  91. data/webpack/move_to_foreman/components/common/{EmptyState → emptyState}/index.js +3 -16
  92. data/webpack/move_to_foreman/components/common/table/components/Table.js +1 -1
  93. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/CollapseSubscriptionGroupButton.test.js.snap +2 -2
  94. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionCell.test.js.snap +1 -1
  95. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionHeaderCell.test.js.snap +1 -1
  96. data/webpack/move_to_pf/LoadingState/LoadingState.js +14 -27
  97. data/webpack/move_to_pf/LoadingState/LoadingState.test.js +4 -8
  98. data/webpack/move_to_pf/react-bootstrap-select/index.js +1 -12
  99. data/webpack/redux/actions/RedHatRepositories/enabled.js +1 -0
  100. data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -5
  101. data/webpack/redux/actions/RedHatRepositories/sets.js +1 -1
  102. data/webpack/redux/consts.js +0 -6
  103. data/webpack/redux/reducers/index.js +0 -2
  104. data/webpack/scenes/RedHatRepositories/components/EnabledRepository.js +23 -14
  105. data/webpack/scenes/RedHatRepositories/components/RepositorySetRepository.js +1 -1
  106. data/webpack/scenes/RedHatRepositories/components/SearchBar.js +0 -1
  107. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/RecommendedRepositorySetsToggler.test.js.snap +1 -3
  108. data/webpack/scenes/RedHatRepositories/index.js +3 -7
  109. data/webpack/scenes/RedHatRepositories/index.scss +0 -1
  110. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailActions.js +8 -3
  111. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailReducer.js +1 -30
  112. data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.js +28 -111
  113. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailReducer.test.js +1 -3
  114. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetails.test.js +1 -6
  115. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +424 -519
  116. data/webpack/scenes/Subscriptions/Details/__tests__/subscriptionDetails.fixtures.js +4 -7
  117. data/webpack/scenes/Subscriptions/Details/index.js +3 -5
  118. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +34 -78
  119. data/webpack/scenes/Subscriptions/Manifest/ManifestActions.js +24 -5
  120. data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryReducer.js +1 -9
  121. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +0 -3
  122. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestActions.test.js +8 -20
  123. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestHistoryReducer.test.js +1 -3
  124. data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +7 -34
  125. data/webpack/scenes/Subscriptions/Manifest/__tests__/manifest.fixtures.js +16 -9
  126. data/webpack/scenes/Subscriptions/Manifest/index.js +0 -1
  127. data/webpack/scenes/Subscriptions/SubscriptionActions.js +26 -5
  128. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +0 -1
  129. data/webpack/scenes/Subscriptions/SubscriptionHelpers.js +0 -3
  130. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +4 -11
  131. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +36 -31
  132. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsActions.js +12 -3
  133. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +27 -57
  134. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsReducer.js +3 -2
  135. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsTableSchema.js +5 -10
  136. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsActions.test.js +5 -10
  137. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsPage.test.js +4 -49
  138. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsReducer.test.js +3 -8
  139. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +11 -21
  140. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/upstreamSubscriptions.fixtures.js +8 -5
  141. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +0 -2
  142. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +3 -9
  143. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +2 -14
  144. data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +17 -11
  145. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/EntitlementsInlineEditFormatter.js +5 -8
  146. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +58 -45
  147. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableHelpers.js +4 -11
  148. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +2 -2
  149. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +3 -16
  150. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +365 -392
  151. data/webpack/scenes/Subscriptions/index.js +0 -1
  152. data/webpack/services/api/fixtures.js +353 -0
  153. data/webpack/services/api/index.js +1 -17
  154. data/webpack/test_setup.js +0 -2
  155. metadata +16 -37
  156. data/config/katello.yml +0 -59
  157. data/webpack/__mocks__/foremanReact/components/BreadcrumbBar.js +0 -3
  158. data/webpack/__mocks__/foremanReact/redux.js +0 -3
  159. data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +0 -8
  160. data/webpack/components/SelectOrg/SelectOrg.scss +0 -3
  161. data/webpack/components/SelectOrg/SelectOrgAction.js +0 -41
  162. data/webpack/components/SelectOrg/SelectOrgReducer.js +0 -33
  163. data/webpack/components/SelectOrg/SetOrganization.js +0 -116
  164. data/webpack/components/WithOrganization/withOrganization.js +0 -28
  165. data/webpack/global_test_setup.js +0 -6
  166. data/webpack/helpers/caret.js +0 -6
  167. data/webpack/move_to_pf/Select/Select.js +0 -40
  168. data/webpack/scenes/Products/ProductActions.js +0 -24
  169. data/webpack/scenes/Products/ProductConstants.js +0 -3
  170. data/webpack/scenes/Products/__tests__/ProductActions.test.js +0 -40
  171. data/webpack/scenes/Products/__tests__/products.fixtures.js +0 -90
  172. data/webpack/scenes/RedHatRepositories/components/EnabledRepositoryContent.js +0 -34
  173. data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepository.test.js +0 -36
  174. data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepositoryContent.test.js +0 -27
  175. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepository.test.js.snap +0 -25
  176. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepositoryContent.test.js.snap +0 -47
  177. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailEnabledProducts.js +0 -54
  178. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProduct.js +0 -29
  179. data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.scss +0 -9
  180. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailEnabledProducts.test.js +0 -18
  181. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailProduct.test.js +0 -13
  182. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailEnabledProducts.test.js.snap +0 -45
  183. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailProduct.test.js.snap +0 -67
  184. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/EntitlementsInlineEditFormatter.test.js +0 -110
  185. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/EntitlementsInlineEditFormatter.test.js.snap +0 -228
  186. data/webpack/scenes/Tasks/helpers.js +0 -52
  187. data/webpack/services/api/testHelpers.js +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 679620f4009df700650bbeb4bde990b172b24bf5
4
- data.tar.gz: bc3615c44ab394145f07e94b1ff48798833fd1c2
3
+ metadata.gz: 9b618c5ea8ea521a3bc6e5c240bb41675162b2a7
4
+ data.tar.gz: 7ee6a2bc9a14c84fb699d749cc05b5956a72ef0b
5
5
  SHA512:
6
- metadata.gz: ddc49c94143661c4b2e189b28d80eca383306cf3d02e522d4f86b28059d72c274f3b4ff847f96f3a52531534197a21547d3da052f53df929ac23d065e632cd32
7
- data.tar.gz: 8d3bb8feb55d123355b9f4267d49513c06bf232b8eca55948a1c844891a8c76836f1b4ffa5955f1eebc86f537f3527ce6e126b608e73167acd40628d5dfcac0e
6
+ metadata.gz: 03e1ed24d9276d21a18323c34be085e1970eff16c2b999e252733b1da420170d54c2689bbb993cc7b68fb75322fb3994089615ecaac7c29efb10a4085637b8f3
7
+ data.tar.gz: 6f5ebc555ebcaadc3b6653979195e0959cc63259327c8dff05c33df1d635081ec97a80a33d0dcc359fdf9156680d0fc14e981c610979fe2590b6a1426aea816f
data/README.md CHANGED
@@ -1,15 +1,14 @@
1
1
  # Katello
2
2
 
3
- [![Build Status](http://ci.theforeman.org/buildStatus/icon?job=test_katello)](http://ci.theforeman.org/job/test_katello)
3
+ [![Build Status](https://ci.theforeman.org/buildStatus/icon?job=test_katello)](https://ci.theforeman.org/job/test_katello)
4
4
  [![Code Climate](https://codeclimate.com/github/Katello/katello/badges/gpa.svg)](https://codeclimate.com/github/Katello/katello)
5
- [![Dependency Status](https://gemnasium.com/Katello/katello.svg)](https://gemnasium.com/Katello/katello)
6
5
 
7
6
  Full documentation is at https://www.theforeman.org/plugins/katello/
8
7
 
9
8
  ## About
10
9
 
11
10
  [Katello](https://www.theforeman.org/plugins/katello/) is a systems life cycle management
12
- plugin to [Foreman](http://www.theforeman.org). Katello allows you to manage
11
+ plugin to [Foreman](https://www.theforeman.org). Katello allows you to manage
13
12
  thousands of machines with one click. Katello can pull content
14
13
  from remote repositories into isolated environments, and make subscriptions
15
14
  management a breeze.
@@ -60,7 +59,7 @@ rake katello:reset
60
59
  ## Found a bug?
61
60
 
62
61
  That's rather unfortunate. But don't worry! We can help. Just file a bug
63
- [in our project tracker](http://projects.theforeman.org/projects/katello).
62
+ [in our project tracker](https://projects.theforeman.org/projects/katello).
64
63
 
65
64
 
66
65
  ## Contributing
@@ -74,9 +73,11 @@ See the [annotation docs](./test/scenarios/README.md) for more information.
74
73
  ## Contact & Resources
75
74
 
76
75
  * [theforeman.org](https://theforeman.org/plugins/katello)
77
- * [Foreman User Mailing List](https://groups.google.com/forum/?fromgroups#!forum/foreman-users)
78
- * [Foreman Developer mailing list](https://groups.google.com/forum/?fromgroups#!forum/foreman-dev)
79
- * [IRC Freenode](http://freenode.net/using_the_network.shtml): #theforeman-dev
76
+ * [Discourse Forum](https://theforeman.org/support.html#DiscourseForum)
77
+ * Archived mailing lists:
78
+ * [Foreman User Mailing List](https://groups.google.com/forum/?fromgroups#!forum/foreman-users)
79
+ * [Foreman Developer mailing list](https://groups.google.com/forum/?fromgroups#!forum/foreman-dev)
80
+ * [IRC Freenode](https://theforeman.org/support.html#IRClivechat): #theforeman-dev, #theforeman
80
81
 
81
82
  ## Documentation
82
83
 
@@ -1,4 +1,3 @@
1
- //= require bastion/lodash/lodash
2
1
  //= require "katello/common/katello.global"
3
2
  //= require "katello/common/katello.common.js"
4
3
  //= require "katello/common/katello"
@@ -1,3 +1,2 @@
1
1
  //= require "katello/jquery.treeTable"
2
- //= require "katello/common/katello.common.js"
3
2
  //= require "katello/sync_management/sync_management"
@@ -0,0 +1,477 @@
1
+ module Katello
2
+ # rubocop:disable Metrics/ClassLength
3
+ class Api::Registry::RegistryProxiesController < Api::V2::ApiController
4
+ before_action :disable_strong_params
5
+ before_action :confirm_settings
6
+ skip_before_action :authorize
7
+ before_action :optional_authorize, only: [:token]
8
+ before_action :registry_authorize, except: [:token, :v1_search]
9
+ before_action :authorize_repository_read, only: [:pull_manifest, :tags_list]
10
+ before_action :authorize_repository_write, only: [:push_manifest]
11
+ skip_before_action :check_content_type, :only => [:start_upload_blob, :upload_blob, :finish_upload_blob,
12
+ :chunk_upload_blob, :push_manifest]
13
+ skip_after_action :log_response_body, :only => [:pull_blob]
14
+
15
+ wrap_parameters false
16
+
17
+ around_action :repackage_message
18
+
19
+ def repackage_message
20
+ yield
21
+ ensure
22
+ response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
23
+ end
24
+
25
+ rescue_from RestClient::Exception do |e|
26
+ Rails.logger.error pp_exception(e)
27
+ if request_from_katello_cli?
28
+ render json: { errors: [e.http_body] }, status: e.http_code
29
+ else
30
+ render plain: e.http_body, status: e.http_code
31
+ end
32
+ end
33
+
34
+ def redirect_authorization_headers
35
+ response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
36
+ response.headers['Www-Authenticate'] = "Bearer realm=\"#{request_url}/v2/token\"," \
37
+ "service=\"#{request.host}\"," \
38
+ "scope=\"repository:registry:pull,push\""
39
+ end
40
+
41
+ def optional_authorize
42
+ @repository = find_scope_repository
43
+ if @repository && @repository.environment.registry_unauthenticated_pull
44
+ true
45
+ else
46
+ authorize
47
+ end
48
+ end
49
+
50
+ def registry_authorize
51
+ @repository = find_readable_repository
52
+ return true if request.method == 'GET' && @repository && @repository.environment.registry_unauthenticated_pull
53
+
54
+ token = request.headers['Authorization']
55
+ if token
56
+ token_type, token = token.split
57
+ if token_type == 'Bearer' && token
58
+ personal_token = PersonalAccessToken.find_by_token(token)
59
+ if personal_token && !personal_token.expired?
60
+ User.current = User.unscoped.find(personal_token.user_id)
61
+ return true if User.current
62
+ end
63
+ elsif token_type == 'Basic' && token
64
+ return true if authorize
65
+ redirect_authorization_headers
66
+ return false
67
+ end
68
+ end
69
+ redirect_authorization_headers
70
+ render_error('unauthorized', :status => :unauthorized)
71
+ return false
72
+ end
73
+
74
+ def find_writable_repository
75
+ Repository.docker_type.syncable.find_by_container_repository_name(params[:repository])
76
+ end
77
+
78
+ def authorize_repository_write
79
+ @repository = find_writable_repository
80
+ unless @repository
81
+ not_found params[:repository]
82
+ return false
83
+ end
84
+ true
85
+ end
86
+
87
+ # Reduce visible repos to include lifecycle env permissions
88
+ # http://projects.theforeman.org/issues/22914
89
+ # Also include repositories in lifecycle environments with registry_unauthenticated_pull=true
90
+ def readable_repositories
91
+ table_name = Repository.table_name
92
+ in_products = Repository.where(:product_id => Katello::Product.authorized(:view_products))
93
+ .select(:id)
94
+ in_environments = Repository.where(:environment_id => Katello::KTEnvironment.authorized(:view_lifecycle_environments)).select(:id)
95
+ in_unauth_environments = Repository.joins(:environment).where("#{Katello::KTEnvironment.table_name}.registry_unauthenticated_pull" => true).select(:id)
96
+ in_content_views = Repository.joins(:content_view_repositories).where("#{ContentViewRepository.table_name}.content_view_id" => Katello::ContentView.readable).select(:id)
97
+ in_versions = Repository.joins(:content_view_version).where("#{Katello::ContentViewVersion.table_name}.content_view_id" => Katello::ContentView.readable).select(:id)
98
+ Repository.where("#{table_name}.id in (?) or #{table_name}.id in (?) or #{table_name}.id in (?) or #{table_name}.id in (?) or #{table_name}.id in (?)", in_products, in_content_views, in_versions, in_environments, in_unauth_environments)
99
+ end
100
+
101
+ def find_readable_repository
102
+ return nil unless params[:repository]
103
+ repository = Repository.docker_type.find_by_container_repository_name(params[:repository])
104
+ if repository && !repository.environment.registry_unauthenticated_pull
105
+ repository = readable_repositories.docker_type.find_by_container_repository_name(params[:repository])
106
+ end
107
+ repository
108
+ end
109
+
110
+ def authorize_repository_read
111
+ @repository = find_readable_repository
112
+ unless @repository
113
+ not_found params[:repository]
114
+ return false
115
+ end
116
+
117
+ if params[:tag]
118
+ if params[:tag][0..6] == 'sha256:'
119
+ manifest = Katello::DockerManifestList.where(digest: params[:tag]).first || Katello::DockerManifest.where(digest: params[:tag]).first
120
+ not_found params[:tag] unless manifest
121
+ else
122
+ tag = DockerMetaTag.where(repository_id: @repository.id, name: params[:tag]).first
123
+ not_found params[:tag] unless tag
124
+ end
125
+ end
126
+
127
+ true
128
+ end
129
+
130
+ def token
131
+ if @repository && @repository.environment.registry_unauthenticated_pull
132
+ personal_token = OpenStruct.new(token: 'unauthenticated', issued_at: Time.now, expires_at: Time.now + 3)
133
+ else
134
+ personal_token = PersonalAccessToken.where(user_id: User.current.id, name: 'registry').first
135
+ if personal_token.nil?
136
+ personal_token = PersonalAccessToken.new(user: User.current, name: 'registry', expires_at: 6.minutes.from_now)
137
+ personal_token.generate_token
138
+ personal_token.save!
139
+ else
140
+ personal_token.expires_at = 6.minutes.from_now
141
+ personal_token.save!
142
+ end
143
+ end
144
+
145
+ response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
146
+ render json: { token: personal_token.token, expires_at: personal_token.expires_at, issued_at: personal_token.created_at }
147
+ end
148
+
149
+ def pull_manifest
150
+ headers = {}
151
+ env = request.env.select do |key, _value|
152
+ key.match("^HTTP.*")
153
+ end
154
+ env.each do |header|
155
+ headers[header[0].split('_')[1..-1].join('-')] = header[1]
156
+ end
157
+
158
+ r = Resources::Registry::Proxy.get(@_request.fullpath, headers)
159
+ logger.debug r
160
+ results = JSON.parse(r)
161
+
162
+ response.header['Docker-Content-Digest'] = "sha256:#{Digest::SHA256.hexdigest(r)}"
163
+ render json: r, content_type: results['mediaType']
164
+ end
165
+
166
+ def check_blob
167
+ begin
168
+ r = Resources::Registry::Proxy.get(@_request.fullpath, 'Accept' => request.headers['Accept'])
169
+ response.header['Content-Length'] = "#{r.body.size}"
170
+ rescue RestClient::NotFound
171
+ digest_file = tmp_file("#{params[:digest][7..-1]}.tar")
172
+ raise unless File.exist? digest_file
173
+ response.header['Content-Length'] = "#{File.size digest_file}"
174
+ end
175
+ render json: {}
176
+ end
177
+
178
+ def pull_blob
179
+ r = Resources::Registry::Proxy.get(@_request.fullpath, 'Accept' => request.headers['Accept'])
180
+ render json: r
181
+ end
182
+
183
+ def push_manifest
184
+ repository = params[:repository]
185
+ tag = params[:tag]
186
+
187
+ manifest = create_manifest
188
+ return if manifest.nil?
189
+
190
+ begin
191
+ files = get_manifest_files(repository, manifest)
192
+ return if files.nil?
193
+
194
+ tar_file = create_tar_file(files, repository, tag)
195
+ return if tar_file.nil?
196
+
197
+ digest = upload_manifest(tar_file)
198
+ return if digest.nil?
199
+
200
+ tag = upload_tag(digest, tag)
201
+ return if tag.nil?
202
+ ensure
203
+ File.delete(tmp_file('manifest.json')) if File.exist? tmp_file('manifest.json')
204
+ end
205
+
206
+ render json: {}
207
+ end
208
+
209
+ def pulp_content
210
+ Katello.pulp_server.resources.content
211
+ end
212
+
213
+ def start_upload_blob
214
+ uuid = SecureRandom.hex(16)
215
+ response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{uuid}"
216
+ response.header['Docker-Upload-UUID'] = uuid
217
+ response.header['Range'] = '0-0'
218
+ head 202
219
+ end
220
+
221
+ def status_upload_blob
222
+ response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{params[:uuid]}"
223
+ response.header['Range'] = "123"
224
+ response.header['Docker-Upload-UUID'] = "123"
225
+ render plain: '', status: 204
226
+ end
227
+
228
+ def chunk_upload_blob
229
+ response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{params[:uuid]}"
230
+ render plain: '', status: 202
231
+ end
232
+
233
+ def upload_blob
234
+ File.open(tmp_file("#{params[:uuid]}.tar"), 'ab', 0600) do |file|
235
+ file.write request.body.read
236
+ end
237
+
238
+ # ???? true chunked data?
239
+ if request.headers['Content-Range']
240
+ render_error 'unprocessable_entity', :status => :unprocessable_entity
241
+ end
242
+
243
+ response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{params[:uuid]}"
244
+ response.header['Range'] = "1-#{request.body.size}"
245
+ response.header['Docker-Upload-UUID'] = params[:uuid]
246
+ head 204
247
+ end
248
+
249
+ def finish_upload_blob
250
+ # error by client if no params[:digest]
251
+
252
+ uuid_file = tmp_file("#{params[:uuid]}.tar")
253
+ digest_file = tmp_file("#{params[:digest][7..-1]}.tar")
254
+
255
+ File.delete(digest_file) if File.exist? digest_file
256
+ File.rename(uuid_file, digest_file)
257
+
258
+ response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/#{params[:digest]}"
259
+ response.header['Docker-Content-Digest'] = params[:digest]
260
+ response.header['Content-Range'] = "1-#{File.size(digest_file)}"
261
+ response.header['Content-Length'] = "0"
262
+ response.header['Docker-Upload-UUID'] = params[:uuid]
263
+ head 201
264
+ end
265
+
266
+ def cancel_upload_blob
267
+ render plain: '', status: 200
268
+ end
269
+
270
+ def ping
271
+ response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
272
+ render json: {}, status: 200
273
+ end
274
+
275
+ def v1_ping
276
+ head 200
277
+ end
278
+
279
+ def v1_search
280
+ authenticate # to set current_user, not to enforce
281
+ options = {
282
+ resource_class: Katello::Repository
283
+ }
284
+ params[:per_page] = params[:n] || 25
285
+ params[:search] = params[:q]
286
+
287
+ search_results = scoped_search(readable_repositories.where(content_type: 'docker').distinct,
288
+ :container_repository_name, :asc, options)
289
+ results = {
290
+ num_results: search_results[:subtotal],
291
+ query: params[:search]
292
+ }
293
+ results[:results] = search_results[:results].collect do |repository|
294
+ { name: repository[:container_repository_name], description: repository[:description] }
295
+ end
296
+ render json: results, status: 200
297
+ end
298
+
299
+ def catalog
300
+ repositories = readable_repositories.where(content_type: 'docker').collect do |repository|
301
+ repository.container_repository_name
302
+ end
303
+ render json: { repositories: repositories }
304
+ end
305
+
306
+ def tags_list
307
+ tags = @repository.docker_tags.collect do |tag|
308
+ tag.name
309
+ end
310
+ tags.uniq!
311
+ tags.sort!
312
+ render json: {
313
+ name: @repository.container_repository_name,
314
+ tags: tags
315
+ }
316
+ end
317
+
318
+ def create_manifest
319
+ filename = tmp_file('manifest.json')
320
+ if File.exist? filename
321
+ render_error('custom_error', :status => :unprocessable_entity,
322
+ :locals => { :message => "Upload already in progress" })
323
+ return nil
324
+ end
325
+ manifest = request.body.read
326
+ File.open(tmp_file('manifest.json'), 'wb', 0600) do |file|
327
+ file.write manifest
328
+ end
329
+ manifest = JSON.parse(manifest)
330
+ rescue
331
+ File.delete(tmp_file('manifest.json')) if File.exist? tmp_file('manifest.json')
332
+ end
333
+
334
+ def get_manifest_files(repository, manifest)
335
+ files = ['manifest.json']
336
+ if manifest['schemaVersion'] == 1
337
+ if manifest['fsLayers']
338
+ files += manifest['fsLayers'].collect do |layer|
339
+ layerfile = "#{layer['blobSum'][7..-1]}.tar"
340
+ force_include_layer(repository, layer['blobSum'], layerfile)
341
+ layerfile
342
+ end
343
+ end
344
+ elsif manifest['schemaVersion'] == 2
345
+ if manifest['layers']
346
+ files += manifest['layers'].collect do |layer|
347
+ layerfile = "#{layer['digest'][7..-1]}.tar"
348
+ force_include_layer(repository, layer['digest'], layerfile)
349
+ layerfile
350
+ end
351
+ end
352
+ files << "#{manifest['config']['digest'][7..-1]}.tar"
353
+ else
354
+ render_error 'custom_error', :status => :internal_server_error,
355
+ :locals => { :message => "Unsupported schema #{manifest['schemaVersion']}" }
356
+ return nil
357
+ end
358
+ files
359
+ end
360
+
361
+ def create_tar_file(files, repository, tag)
362
+ tar_file = "#{repository}_#{tag}.tar"
363
+ `/usr/bin/tar cf #{tmp_file(tar_file)} -C #{tmp_dir} #{files.join(' ')}`
364
+
365
+ files.each do |file|
366
+ filename = tmp_file(file)
367
+ File.delete(filename) if File.exist? filename
368
+ end
369
+ tar_file
370
+ end
371
+
372
+ def upload_manifest(tar_file)
373
+ upload_id = pulp_content.create_upload_request['upload_id']
374
+ filename = tmp_file(tar_file)
375
+ File.open(filename, 'rb') do |file|
376
+ pulp_content.upload_bits(upload_id, 0, file.read)
377
+
378
+ file.rewind
379
+ content = file.read
380
+ unit_keys = [{
381
+ name: filename,
382
+ size: file.size,
383
+ checksum: Digest::SHA256.hexdigest(content)
384
+ }]
385
+ unit_type_id = 'docker_manifest'
386
+ task = sync_task(::Actions::Katello::Repository::ImportUpload,
387
+ @repository, [upload_id], :unit_type_id => unit_type_id,
388
+ :unit_keys => unit_keys,
389
+ :generate_metadata => true, :sync_capsule => true)
390
+ digest = task.output['upload_results'][0]['digest']
391
+
392
+ File.delete(filename)
393
+
394
+ digest
395
+ end
396
+ ensure
397
+ pulp_content.delete_upload_request(upload_id) if upload_id
398
+ end
399
+
400
+ def upload_tag(digest, tag)
401
+ upload_id = pulp_content.create_upload_request['upload_id']
402
+ unit_keys = [{
403
+ name: tag,
404
+ digest: digest
405
+ }]
406
+ unit_type_id = 'docker_tag'
407
+ sync_task(::Actions::Katello::Repository::ImportUpload,
408
+ @repository, [upload_id], :unit_type_id => unit_type_id,
409
+ :unit_keys => unit_keys,
410
+ :generate_metadata => true, :sync_capsule => true)
411
+
412
+ tag
413
+ ensure
414
+ pulp_content.delete_upload_request(upload_id) if upload_id
415
+ end
416
+
417
+ def tmp_dir
418
+ "#{Rails.root}/tmp"
419
+ end
420
+
421
+ def tmp_file(filename)
422
+ File.join(tmp_dir, filename)
423
+ end
424
+
425
+ # TODO: Until pulp supports optional upload of layers, include all layers
426
+ # https://pulp.plan.io/issues/3497
427
+ def force_include_layer(repository, digest, layer)
428
+ unless File.exist? tmp_file(layer)
429
+ logger.debug "Getting blob #{digest} to write to #{layer}"
430
+ fullpath = "/v2/#{repository}/blobs/#{digest}"
431
+ request = Resources::Registry::Proxy.get(fullpath)
432
+ File.open(tmp_file(layer), 'wb', 0600) do |file|
433
+ file.write request.body
434
+ end
435
+ logger.debug "Wrote blob #{digest} to #{layer}"
436
+ end
437
+ end
438
+
439
+ def find_scope_repository
440
+ scope = params['scope']
441
+ return nil unless scope
442
+
443
+ scopes = scope.split(':')
444
+ scopes[2] == 'pull' ? Repository.docker_type.find_by_container_repository_name(scopes[1]) : nil
445
+ end
446
+
447
+ def disable_strong_params
448
+ params.permit!
449
+ end
450
+
451
+ def confirm_settings
452
+ return true if SETTINGS[:katello][:registry]
453
+ render_error('custom_error', :status => :not_found,
454
+ :locals => { :message => "Registry not configured" })
455
+ false
456
+ end
457
+
458
+ def request_url
459
+ request.protocol + request.host_with_port
460
+ end
461
+
462
+ def logger
463
+ ::Foreman::Logging.logger('katello/registry_proxy')
464
+ end
465
+
466
+ def route_name
467
+ Engine.routes.router.recognize(request) do |_, params|
468
+ break params[:action] if params[:action]
469
+ end
470
+ end
471
+
472
+ def process_action(method_name, *args)
473
+ ::Api::V2::BaseController.instance_method(:process_action).bind(self).call(method_name, *args)
474
+ Rails.logger.debug "With body: #{response.body}\n" unless route_name == 'pull_blob'
475
+ end
476
+ end
477
+ end