katello 4.19.0.rc1 → 4.19.0.rc2

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.

Potentially problematic release.


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

Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/locale/bn/katello.js +337 -16
  3. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +337 -16
  4. data/app/assets/javascripts/katello/locale/ca/katello.js +337 -16
  5. data/app/assets/javascripts/katello/locale/cs/katello.js +338 -17
  6. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +337 -16
  7. data/app/assets/javascripts/katello/locale/de/katello.js +339 -18
  8. data/app/assets/javascripts/katello/locale/de_AT/katello.js +337 -16
  9. data/app/assets/javascripts/katello/locale/de_DE/katello.js +337 -16
  10. data/app/assets/javascripts/katello/locale/el/katello.js +337 -16
  11. data/app/assets/javascripts/katello/locale/en/katello.js +338 -17
  12. data/app/assets/javascripts/katello/locale/en_GB/katello.js +337 -16
  13. data/app/assets/javascripts/katello/locale/en_US/katello.js +337 -16
  14. data/app/assets/javascripts/katello/locale/es/katello.js +337 -16
  15. data/app/assets/javascripts/katello/locale/et_EE/katello.js +337 -16
  16. data/app/assets/javascripts/katello/locale/fr/katello.js +340 -19
  17. data/app/assets/javascripts/katello/locale/gl/katello.js +337 -16
  18. data/app/assets/javascripts/katello/locale/gu/katello.js +337 -16
  19. data/app/assets/javascripts/katello/locale/he_IL/katello.js +337 -16
  20. data/app/assets/javascripts/katello/locale/hi/katello.js +337 -16
  21. data/app/assets/javascripts/katello/locale/id/katello.js +337 -16
  22. data/app/assets/javascripts/katello/locale/it/katello.js +337 -16
  23. data/app/assets/javascripts/katello/locale/ja/katello.js +340 -19
  24. data/app/assets/javascripts/katello/locale/ka/katello.js +339 -18
  25. data/app/assets/javascripts/katello/locale/kn/katello.js +337 -16
  26. data/app/assets/javascripts/katello/locale/ko/katello.js +339 -18
  27. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +337 -16
  28. data/app/assets/javascripts/katello/locale/mr/katello.js +337 -16
  29. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +337 -16
  30. data/app/assets/javascripts/katello/locale/or/katello.js +337 -16
  31. data/app/assets/javascripts/katello/locale/pa/katello.js +337 -16
  32. data/app/assets/javascripts/katello/locale/pl/katello.js +337 -16
  33. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +337 -16
  34. data/app/assets/javascripts/katello/locale/pt/katello.js +337 -16
  35. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +337 -16
  36. data/app/assets/javascripts/katello/locale/ro/katello.js +337 -16
  37. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +337 -16
  38. data/app/assets/javascripts/katello/locale/ru/katello.js +337 -16
  39. data/app/assets/javascripts/katello/locale/sl/katello.js +337 -16
  40. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +337 -16
  41. data/app/assets/javascripts/katello/locale/ta/katello.js +337 -16
  42. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +337 -16
  43. data/app/assets/javascripts/katello/locale/te/katello.js +337 -16
  44. data/app/assets/javascripts/katello/locale/tr/katello.js +337 -16
  45. data/app/assets/javascripts/katello/locale/vi/katello.js +337 -16
  46. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +337 -16
  47. data/app/assets/javascripts/katello/locale/zh/katello.js +337 -16
  48. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +340 -19
  49. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +337 -16
  50. data/app/controllers/katello/concerns/api/v2/hosts_controller_extensions.rb +10 -0
  51. data/app/services/katello/pulp3/repository/apt.rb +12 -4
  52. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn.po +10 -0
  53. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn_IN.po +10 -0
  54. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ca.po +10 -0
  55. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/cs_CZ.po +10 -0
  56. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de.po +10 -0
  57. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_AT.po +10 -0
  58. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_DE.po +10 -0
  59. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/el.po +10 -0
  60. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_GB.po +10 -0
  61. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_US.po +10 -0
  62. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/es.po +10 -0
  63. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/et_EE.po +10 -0
  64. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/fr.po +10 -0
  65. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gl.po +10 -0
  66. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gu.po +10 -0
  67. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/he_IL.po +10 -0
  68. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/hi.po +10 -0
  69. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/id.po +10 -0
  70. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/it.po +10 -0
  71. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ja.po +10 -0
  72. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ka.po +10 -0
  73. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/kn.po +10 -0
  74. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ko.po +10 -0
  75. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ml_IN.po +10 -0
  76. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/mr.po +10 -0
  77. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/nl_NL.po +10 -0
  78. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/or.po +10 -0
  79. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pa.po +10 -0
  80. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl.po +10 -0
  81. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl_PL.po +10 -0
  82. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt.po +10 -0
  83. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt_BR.po +10 -0
  84. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro.po +10 -0
  85. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro_RO.po +10 -0
  86. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ru.po +10 -0
  87. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sl.po +10 -0
  88. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sv_SE.po +10 -0
  89. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta.po +10 -0
  90. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta_IN.po +10 -0
  91. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/te.po +10 -0
  92. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/tr.po +10 -0
  93. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi.po +10 -0
  94. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi_VN.po +10 -0
  95. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh.po +10 -0
  96. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_CN.po +10 -0
  97. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_TW.po +10 -0
  98. data/lib/katello/plugin.rb +1 -0
  99. data/lib/katello/version.rb +1 -1
  100. data/locale/action_names.rb +180 -0
  101. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  102. data/locale/bn/katello.po +337 -16
  103. data/locale/bn_IN/LC_MESSAGES/katello.mo +0 -0
  104. data/locale/bn_IN/katello.po +337 -16
  105. data/locale/ca/LC_MESSAGES/katello.mo +0 -0
  106. data/locale/ca/katello.po +337 -16
  107. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  108. data/locale/cs/katello.po +338 -18
  109. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  110. data/locale/cs_CZ/katello.po +337 -16
  111. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  112. data/locale/de/katello.po +340 -19
  113. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  114. data/locale/de_AT/katello.po +337 -16
  115. data/locale/de_DE/LC_MESSAGES/katello.mo +0 -0
  116. data/locale/de_DE/katello.po +337 -16
  117. data/locale/el/LC_MESSAGES/katello.mo +0 -0
  118. data/locale/el/katello.po +337 -16
  119. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  120. data/locale/en/katello.po +338 -18
  121. data/locale/en_GB/LC_MESSAGES/katello.mo +0 -0
  122. data/locale/en_GB/katello.po +337 -16
  123. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  124. data/locale/en_US/katello.po +337 -16
  125. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  126. data/locale/es/katello.po +337 -16
  127. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  128. data/locale/et_EE/katello.po +337 -16
  129. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  130. data/locale/fr/katello.po +341 -20
  131. data/locale/gl/LC_MESSAGES/katello.mo +0 -0
  132. data/locale/gl/katello.po +337 -16
  133. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  134. data/locale/gu/katello.po +337 -16
  135. data/locale/he_IL/LC_MESSAGES/katello.mo +0 -0
  136. data/locale/he_IL/katello.po +337 -16
  137. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  138. data/locale/hi/katello.po +337 -16
  139. data/locale/id/LC_MESSAGES/katello.mo +0 -0
  140. data/locale/id/katello.po +337 -16
  141. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  142. data/locale/it/katello.po +337 -16
  143. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  144. data/locale/ja/katello.po +342 -20
  145. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  146. data/locale/ka/katello.po +339 -18
  147. data/locale/katello.pot +748 -233
  148. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  149. data/locale/kn/katello.po +337 -16
  150. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  151. data/locale/ko/katello.po +340 -19
  152. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  153. data/locale/ml_IN/katello.po +337 -16
  154. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  155. data/locale/mr/katello.po +337 -16
  156. data/locale/nl_NL/LC_MESSAGES/katello.mo +0 -0
  157. data/locale/nl_NL/katello.po +337 -16
  158. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  159. data/locale/or/katello.po +337 -16
  160. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  161. data/locale/pa/katello.po +337 -16
  162. data/locale/pl/LC_MESSAGES/katello.mo +0 -0
  163. data/locale/pl/katello.po +337 -16
  164. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  165. data/locale/pl_PL/katello.po +337 -16
  166. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  167. data/locale/pt/katello.po +337 -16
  168. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  169. data/locale/pt_BR/katello.po +337 -16
  170. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  171. data/locale/ro/katello.po +337 -16
  172. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  173. data/locale/ro_RO/katello.po +337 -16
  174. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  175. data/locale/ru/katello.po +337 -16
  176. data/locale/sl/LC_MESSAGES/katello.mo +0 -0
  177. data/locale/sl/katello.po +337 -16
  178. data/locale/sv_SE/LC_MESSAGES/katello.mo +0 -0
  179. data/locale/sv_SE/katello.po +337 -16
  180. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  181. data/locale/ta/katello.po +337 -16
  182. data/locale/ta_IN/LC_MESSAGES/katello.mo +0 -0
  183. data/locale/ta_IN/katello.po +337 -16
  184. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  185. data/locale/te/katello.po +337 -16
  186. data/locale/tr/LC_MESSAGES/katello.mo +0 -0
  187. data/locale/tr/katello.po +337 -16
  188. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  189. data/locale/vi/katello.po +337 -16
  190. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  191. data/locale/vi_VN/katello.po +337 -16
  192. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  193. data/locale/zh/katello.po +337 -16
  194. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  195. data/locale/zh_CN/katello.po +341 -20
  196. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  197. data/locale/zh_TW/katello.po +337 -16
  198. data/webpack/ForemanColumnExtensions/index.js +46 -2
  199. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/02_BulkErrataTable.js +1 -1
  200. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/02_BulkPackagesTable.js +1 -1
  201. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/01_BulkRepositorySetsTable.js +1 -1
  202. data/webpack/components/extensions/Hosts/BulkActions/HostReview.js +1 -1
  203. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +15 -5
  204. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +22 -5
  205. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +141 -0
  206. metadata +6 -8
@@ -12,6 +12,7 @@ import {
12
12
  FlexItem,
13
13
  Popover,
14
14
  Badge,
15
+ Button,
15
16
  DescriptionList,
16
17
  DescriptionListGroup,
17
18
  DescriptionListDescription as Dd,
@@ -206,9 +207,9 @@ const hostsIndexColumnExtensions = [
206
207
  </Flex>
207
208
  }
208
209
  >
209
- <FlexItem>
210
+ <Button variant="plain" style={{ padding: 0, color: 'inherit' }} ouiaId="content-view-environments-button">
210
211
  {truncate(contentViewEnvironments.map(cve => cve.label).join(', '), 35)}
211
- </FlexItem>
212
+ </Button>
212
213
  </Popover>
213
214
  </Flex>
214
215
  );
@@ -237,6 +238,49 @@ const hostsIndexColumnExtensions = [
237
238
  weight: 2600,
238
239
  isSorted: true,
239
240
  },
241
+ {
242
+ columnName: 'host_collections',
243
+ title: __('Host collections'),
244
+ wrapper: (hostDetails) => {
245
+ const hostCollections = hostDetails?.host_collections ?? [];
246
+ if (hostCollections.length === 0) return '—';
247
+
248
+ // Show count badge if more than one collection
249
+ if (hostCollections.length > 1) {
250
+ return (
251
+ <Flex>
252
+ <FlexItem>
253
+ <Badge isRead>{hostCollections.length}</Badge>
254
+ </FlexItem>
255
+ <Popover
256
+ id="host-collections-tooltip"
257
+ className="host-collections-tooltip"
258
+ maxWidth="34rem"
259
+ headerContent={hostDetails.display_name}
260
+ bodyContent={
261
+ <Flex direction={{ default: 'column' }}>
262
+ {hostCollections.map((hc, index) => (
263
+ <FlexItem key={hc.id}>
264
+ <Text component={TextVariants.p} ouiaId={`host-collection-name-${index}`}>{hc.name}</Text>
265
+ </FlexItem>
266
+ ))}
267
+ </Flex>
268
+ }
269
+ >
270
+ <Button variant="plain" style={{ padding: 0, color: 'inherit' }} ouiaId="host-collections-button">
271
+ {truncate(hostCollections.map(hc => hc.name).join(', '), 35)}
272
+ </Button>
273
+ </Popover>
274
+ </Flex>
275
+ );
276
+ }
277
+
278
+ // Just show the name if only one collection
279
+ return truncate(hostCollections[0].name, 35);
280
+ },
281
+ weight: 2700,
282
+ isSorted: false,
283
+ },
240
284
  ];
241
285
 
242
286
  hostsIndexColumnExtensions.forEach((column) => {
@@ -11,7 +11,7 @@ import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableInde
11
11
  import { translate as __ } from 'foremanReact/common/I18n';
12
12
  import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
13
13
  import { STATUS, getControllerSearchProps } from 'foremanReact/constants';
14
- import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
14
+ import { RowSelectTd } from 'foremanReact/components/PF4/TableIndexPage/RowSelectTd';
15
15
  import { getPageStats } from 'foremanReact/components/PF4/TableIndexPage/Table/helpers';
16
16
  import { BulkErrataWizardContext, ERRATA_URL } from './BulkErrataWizard';
17
17
  import { ErrataType, ErrataSeverity } from '../../../../../components/Errata';
@@ -12,7 +12,7 @@ import { translate as __ } from 'foremanReact/common/I18n';
12
12
  import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
13
13
  import { getControllerSearchProps } from 'foremanReact/constants';
14
14
  import { noop } from 'foremanReact/common/helpers';
15
- import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
15
+ import { RowSelectTd } from 'foremanReact/components/PF4/TableIndexPage/RowSelectTd';
16
16
  import { getPageStats } from 'foremanReact/components/PF4/TableIndexPage/Table/helpers';
17
17
  import { BulkPackagesWizardContext, getPackagesUrl } from './BulkPackagesWizard';
18
18
  import katelloApi from '../../../../../services/api';
@@ -14,7 +14,7 @@ import { useSet } from 'foremanReact/components/PF4/TableIndexPage/Table/TableHo
14
14
  import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
15
15
  import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
16
16
  import { translate as __ } from 'foremanReact/common/I18n';
17
- import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
17
+ import { RowSelectTd } from 'foremanReact/components/PF4/TableIndexPage/RowSelectTd';
18
18
  import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
19
19
  import { noop } from 'foremanReact/common/helpers';
20
20
 
@@ -11,7 +11,7 @@ import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableInde
11
11
  import { HOSTS_API_PATH } from 'foremanReact/routes/Hosts/constants';
12
12
  import { translate as __ } from 'foremanReact/common/I18n';
13
13
  import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
14
- import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
14
+ import { RowSelectTd } from 'foremanReact/components/PF4/TableIndexPage/RowSelectTd';
15
15
  import { getPageStats } from 'foremanReact/components/PF4/TableIndexPage/Table/helpers';
16
16
 
17
17
  const HostReview = ({
@@ -115,15 +115,25 @@ export default (state = initialState, action) => {
115
115
  });
116
116
  }
117
117
 
118
- case SUBSCRIPTIONS_FAILURE:
118
+ case SUBSCRIPTIONS_FAILURE: {
119
+ // Try multiple paths to extract permission errors
120
+ const explicitMissingPermissions = get(action, ['payload', 'messages', 0, 'missing_permissions']);
121
+ const statusCode = get(action, ['payload', 'result', 'response', 'status']);
122
+ const errorMessages = get(action, ['payload', 'messages'], []);
123
+
124
+ // If we got a 403 Forbidden or 404 Not Found, treat it as a permission error
125
+ let missingPermissions = explicitMissingPermissions;
126
+ if (!missingPermissions && (statusCode === 403 || statusCode === 404)) {
127
+ // Use error messages as missing permissions
128
+ missingPermissions = errorMessages.length > 0 ? errorMessages : ['view_subscriptions'];
129
+ }
130
+
119
131
  return state
120
132
  .set('loading', false)
121
133
  .set('results', [])
122
134
  .set('itemCount', 0)
123
- .set(
124
- 'missingPermissions',
125
- get(action, ['payload', 'messages', 0, 'missing_permissions']),
126
- );
135
+ .set('missingPermissions', missingPermissions);
136
+ }
127
137
 
128
138
  case SUBSCRIPTIONS_QUANTITIES_REQUEST:
129
139
  return state.merge({
@@ -32,11 +32,9 @@ class SubscriptionsPage extends Component {
32
32
 
33
33
  componentDidMount() {
34
34
  this.props.resetTasks();
35
-
36
- const { id } = this.props.organization;
37
- if (id) { // navigating from another react page
38
- this.loadData();
39
- }
35
+ // Always load data, even if organization doesn't have an ID yet
36
+ // This allows us to detect permission errors
37
+ this.loadData();
40
38
  }
41
39
 
42
40
  componentDidUpdate(prevProps) {
@@ -137,7 +135,21 @@ class SubscriptionsPage extends Component {
137
135
  deleteButtonDisabled, disableDeleteButton, enableDeleteButton,
138
136
  searchQuery, updateSearchQuery, hasUpstreamConnection,
139
137
  task, activePermissions, subscriptions, subscriptionTableSettings, isManifestImported,
138
+ organization,
140
139
  } = this.props;
140
+
141
+ // If organization failed to load (404/403), the user doesn't have
142
+ // permission to view this organization. Show permission denied
143
+ // regardless of whether subscriptions returned results
144
+ if (organization?.error && !organization.loading) {
145
+ const statusCode = organization.error.response?.status;
146
+
147
+ if (statusCode === 404 || statusCode === 403) {
148
+ const errorMessage = 'You do not have permission to view this organization.';
149
+ return <PermissionDenied missingPermissions={[errorMessage]} />;
150
+ }
151
+ }
152
+
141
153
  // Basic permissions - should we even show this page?
142
154
  if (subscriptions.missingPermissions && subscriptions.missingPermissions.length > 0) {
143
155
  return <PermissionDenied missingPermissions={subscriptions.missingPermissions} />;
@@ -345,6 +357,11 @@ SubscriptionsPage.propTypes = {
345
357
  uuid: PropTypes.string,
346
358
  }),
347
359
  }),
360
+ error: PropTypes.shape({
361
+ response: PropTypes.shape({
362
+ status: PropTypes.number,
363
+ }),
364
+ }),
348
365
  }),
349
366
  task: PropTypes.shape({
350
367
  id: PropTypes.string,
@@ -106,6 +106,147 @@ describe('subscriptions page', () => {
106
106
  expect(toJson(permissionDeniedPage)).toMatchSnapshot();
107
107
  });
108
108
 
109
+ it('should render <PermissionDenied /> when organization load fails with 403', async () => {
110
+ const orgWith403Error = {
111
+ loading: false,
112
+ error: {
113
+ response: {
114
+ status: 403,
115
+ },
116
+ },
117
+ };
118
+ const pageWithOrgError = shallow(<SubscriptionsPage
119
+ setModalOpen={noop}
120
+ setModalClosed={noop}
121
+ organization={orgWith403Error}
122
+ subscriptions={successState}
123
+ subscriptionTableSettings={settingsSuccessState}
124
+ loadTables={loadTables}
125
+ loadTableColumns={loadTableColumns}
126
+ createColumns={createColumns}
127
+ updateColumns={updateColumns}
128
+ loadSubscriptions={loadSubscriptions}
129
+ loadAvailableQuantities={loadAvailableQuantities}
130
+ pingUpstreamSubscriptions={pingUpstreamSubscriptions}
131
+ checkSimpleContentAccessEligible={checkSimpleContentAccessEligible}
132
+ updateQuantity={updateQuantity}
133
+ handleStartTask={handleStartTask}
134
+ handleFinishedTask={handleFinishedTask}
135
+ pollTaskUntilDone={noop}
136
+ pollBulkSearch={noop}
137
+ pollTasks={pollTasks}
138
+ cancelPollTasks={noop}
139
+ deleteSubscriptions={() => {}}
140
+ resetTasks={noop}
141
+ uploadManifest={noop}
142
+ deleteManifest={noop}
143
+ refreshManifest={noop}
144
+ updateSearchQuery={noop}
145
+ openManageManifestModal={noop}
146
+ closeManageManifestModal={noop}
147
+ openDeleteModal={noop}
148
+ closeDeleteModal={noop}
149
+ disableDeleteButton={noop}
150
+ enableDeleteButton={noop}
151
+ />);
152
+ expect(pageWithOrgError.find('PermissionDenied')).toHaveLength(1);
153
+ });
154
+
155
+ it('should render <PermissionDenied /> when organization load fails with 404', async () => {
156
+ const orgWith404Error = {
157
+ loading: false,
158
+ error: {
159
+ response: {
160
+ status: 404,
161
+ },
162
+ },
163
+ };
164
+ const pageWithOrgError = shallow(<SubscriptionsPage
165
+ setModalOpen={noop}
166
+ setModalClosed={noop}
167
+ organization={orgWith404Error}
168
+ subscriptions={successState}
169
+ subscriptionTableSettings={settingsSuccessState}
170
+ loadTables={loadTables}
171
+ loadTableColumns={loadTableColumns}
172
+ createColumns={createColumns}
173
+ updateColumns={updateColumns}
174
+ loadSubscriptions={loadSubscriptions}
175
+ loadAvailableQuantities={loadAvailableQuantities}
176
+ pingUpstreamSubscriptions={pingUpstreamSubscriptions}
177
+ checkSimpleContentAccessEligible={checkSimpleContentAccessEligible}
178
+ updateQuantity={updateQuantity}
179
+ handleStartTask={handleStartTask}
180
+ handleFinishedTask={handleFinishedTask}
181
+ pollTaskUntilDone={noop}
182
+ pollBulkSearch={noop}
183
+ pollTasks={pollTasks}
184
+ cancelPollTasks={noop}
185
+ deleteSubscriptions={() => {}}
186
+ resetTasks={noop}
187
+ uploadManifest={noop}
188
+ deleteManifest={noop}
189
+ refreshManifest={noop}
190
+ updateSearchQuery={noop}
191
+ openManageManifestModal={noop}
192
+ closeManageManifestModal={noop}
193
+ openDeleteModal={noop}
194
+ closeDeleteModal={noop}
195
+ disableDeleteButton={noop}
196
+ enableDeleteButton={noop}
197
+ />);
198
+
199
+ expect(pageWithOrgError.find('PermissionDenied')).toHaveLength(1);
200
+ });
201
+
202
+ it('should not render <PermissionDenied /> when organization is still loading', async () => {
203
+ const orgStillLoading = {
204
+ loading: true,
205
+ error: {
206
+ response: {
207
+ status: 403,
208
+ },
209
+ },
210
+ };
211
+ const pageWithLoadingOrg = shallow(<SubscriptionsPage
212
+ setModalOpen={noop}
213
+ setModalClosed={noop}
214
+ organization={orgStillLoading}
215
+ subscriptions={successState}
216
+ subscriptionTableSettings={settingsSuccessState}
217
+ loadTables={loadTables}
218
+ loadTableColumns={loadTableColumns}
219
+ createColumns={createColumns}
220
+ updateColumns={updateColumns}
221
+ loadSubscriptions={loadSubscriptions}
222
+ loadAvailableQuantities={loadAvailableQuantities}
223
+ pingUpstreamSubscriptions={pingUpstreamSubscriptions}
224
+ checkSimpleContentAccessEligible={checkSimpleContentAccessEligible}
225
+ updateQuantity={updateQuantity}
226
+ handleStartTask={handleStartTask}
227
+ handleFinishedTask={handleFinishedTask}
228
+ pollTaskUntilDone={noop}
229
+ pollBulkSearch={noop}
230
+ pollTasks={pollTasks}
231
+ cancelPollTasks={noop}
232
+ deleteSubscriptions={() => {}}
233
+ resetTasks={noop}
234
+ uploadManifest={noop}
235
+ deleteManifest={noop}
236
+ refreshManifest={noop}
237
+ updateSearchQuery={noop}
238
+ openManageManifestModal={noop}
239
+ closeManageManifestModal={noop}
240
+ openDeleteModal={noop}
241
+ closeDeleteModal={noop}
242
+ disableDeleteButton={noop}
243
+ enableDeleteButton={noop}
244
+ />);
245
+
246
+ // Should not show PermissionDenied while organization is still loading
247
+ expect(pageWithLoadingOrg.find('PermissionDenied')).toHaveLength(0);
248
+ });
249
+
109
250
  it('should poll tasks when org changes', async () => {
110
251
  page.setProps({ organization: { id: 1 } });
111
252
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katello
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.19.0.rc1
4
+ version: 4.19.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - N/A
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-11-11 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -4456,6 +4455,7 @@ files:
4456
4455
  - lib/proxy_api/pulp_node.rb
4457
4456
  - locale/Makefile
4458
4457
  - locale/README
4458
+ - locale/action_names.rb
4459
4459
  - locale/bn/LC_MESSAGES/katello.mo
4460
4460
  - locale/bn/katello.po
4461
4461
  - locale/bn/katello.po.time_stamp
@@ -5613,7 +5613,6 @@ homepage: http://www.katello.org
5613
5613
  licenses:
5614
5614
  - GPL-2.0
5615
5615
  metadata: {}
5616
- post_install_message:
5617
5616
  rdoc_options: []
5618
5617
  require_paths:
5619
5618
  - lib
@@ -5627,12 +5626,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
5627
5626
  version: '4'
5628
5627
  required_rubygems_version: !ruby/object:Gem::Requirement
5629
5628
  requirements:
5630
- - - ">"
5629
+ - - ">="
5631
5630
  - !ruby/object:Gem::Version
5632
- version: 1.3.1
5631
+ version: '0'
5633
5632
  requirements: []
5634
- rubygems_version: 3.2.33
5635
- signing_key:
5633
+ rubygems_version: 3.6.9
5636
5634
  specification_version: 4
5637
5635
  summary: Content and Subscription Management plugin for Foreman
5638
5636
  test_files: []