@rancher/shell 3.0.4 → 3.0.5-rc.2

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 (270) hide show
  1. package/assets/images/providers/sks.svg +1 -0
  2. package/assets/styles/base/_basic.scss +6 -0
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +1 -0
  5. package/assets/styles/global/_button.scss +1 -0
  6. package/assets/translations/en-us.yaml +65 -15
  7. package/assets/translations/zh-hans.yaml +4 -3
  8. package/chart/monitoring/index.vue +3 -1
  9. package/cloud-credential/aws.vue +2 -0
  10. package/components/ActionDropdownShell.vue +71 -0
  11. package/components/AppModal.vue +18 -4
  12. package/components/AsyncButton.vue +24 -7
  13. package/components/BannerGraphic.vue +1 -0
  14. package/components/CommunityLinks.vue +4 -59
  15. package/components/CopyToClipboardText.vue +2 -1
  16. package/components/CruResource.vue +6 -1
  17. package/components/DetailText.vue +5 -0
  18. package/components/ExplorerMembers.vue +1 -1
  19. package/components/ExplorerProjectsNamespaces.vue +68 -18
  20. package/components/GlobalRoleBindings.vue +5 -1
  21. package/components/GrowlManager.vue +1 -0
  22. package/components/LandingPagePreference.vue +7 -3
  23. package/components/LocaleSelector.vue +39 -95
  24. package/components/ModalManager.vue +55 -0
  25. package/components/ModalWithCard.vue +1 -0
  26. package/components/PromptModal.vue +47 -8
  27. package/components/PromptRemove.vue +1 -0
  28. package/components/PromptRestore.vue +1 -0
  29. package/components/ResourceCancelModal.vue +1 -0
  30. package/components/ResourceDetail/Masthead.vue +38 -12
  31. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  32. package/components/ResourceDetail/index.vue +47 -12
  33. package/components/ResourceTable.vue +54 -19
  34. package/components/SideNav.vue +5 -1
  35. package/components/SlideInPanelManager.vue +126 -0
  36. package/components/SortableTable/THead.vue +5 -2
  37. package/components/SortableTable/actions.js +1 -1
  38. package/components/SortableTable/index.vue +64 -51
  39. package/components/SortableTable/paging.js +16 -19
  40. package/components/SortableTable/selection.js +0 -11
  41. package/components/Wizard.vue +2 -2
  42. package/components/__tests__/AsyncButton.test.ts +2 -2
  43. package/components/__tests__/ModalManager.spec.ts +176 -0
  44. package/components/__tests__/PromptModal.test.ts +148 -0
  45. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  46. package/components/auth/AuthBanner.vue +13 -11
  47. package/components/auth/Principal.vue +1 -0
  48. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  49. package/components/auth/login/ldap.vue +1 -1
  50. package/components/fleet/FleetResources.vue +21 -6
  51. package/components/form/ArrayList.vue +76 -60
  52. package/components/form/BannerSettings.vue +17 -2
  53. package/components/form/ColorInput.vue +35 -6
  54. package/components/form/Command.vue +6 -15
  55. package/components/form/EnvVars.vue +16 -8
  56. package/components/form/HealthCheck.vue +3 -3
  57. package/components/form/HookOption.vue +11 -16
  58. package/components/form/LabeledSelect.vue +18 -22
  59. package/components/form/LifecycleHooks.vue +3 -3
  60. package/components/form/MatchExpressions.vue +14 -8
  61. package/components/form/NameNsDescription.vue +128 -104
  62. package/components/form/Networking.vue +20 -12
  63. package/components/form/NodeAffinity.vue +31 -23
  64. package/components/form/NodeScheduling.vue +13 -3
  65. package/components/form/NotificationSettings.vue +15 -1
  66. package/components/form/Password.vue +1 -0
  67. package/components/form/PodAffinity.vue +43 -43
  68. package/components/form/Probe.vue +68 -66
  69. package/components/form/ResourceQuota/Project.vue +5 -1
  70. package/components/form/ResourceSelector.vue +7 -9
  71. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +16 -24
  72. package/components/form/SSHKnownHosts/index.vue +30 -13
  73. package/components/form/Security.vue +54 -56
  74. package/components/form/Select.vue +32 -21
  75. package/components/form/ShellInput.vue +5 -1
  76. package/components/form/Tolerations.vue +5 -1
  77. package/components/form/ValueFromResource.vue +134 -121
  78. package/components/form/WorkloadPorts.vue +18 -18
  79. package/components/form/__tests__/ArrayList.test.ts +5 -2
  80. package/components/form/__tests__/ColorInput.test.ts +35 -0
  81. package/components/form/__tests__/LabeledSelect.test.ts +40 -0
  82. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  83. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  84. package/components/form/__tests__/Probe.test.ts +12 -8
  85. package/components/form/__tests__/SSHKnownHosts.test.ts +22 -2
  86. package/components/form/__tests__/Select.test.ts +37 -0
  87. package/components/formatter/InternalExternalIP.vue +2 -0
  88. package/components/formatter/SecretData.vue +20 -7
  89. package/components/nav/Group.vue +27 -5
  90. package/components/nav/Header.vue +17 -43
  91. package/components/nav/NamespaceFilter.vue +134 -86
  92. package/components/nav/TopLevelMenu.vue +4 -5
  93. package/components/nav/Type.vue +12 -1
  94. package/components/nav/WindowManager/ContainerLogs.vue +87 -61
  95. package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
  96. package/components/templates/blank.vue +4 -1
  97. package/components/templates/default.vue +8 -3
  98. package/components/templates/home.vue +10 -1
  99. package/components/templates/plain.vue +10 -4
  100. package/composables/focusTrap.ts +12 -4
  101. package/composables/useRuntimeFlag.ts +29 -0
  102. package/config/router/routes.js +20 -13
  103. package/config/store.js +4 -0
  104. package/config/uiplugins.js +5 -1
  105. package/core/types.ts +12 -6
  106. package/detail/catalog.cattle.io.app.vue +6 -1
  107. package/detail/fleet.cattle.io.bundle.vue +70 -6
  108. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  109. package/detail/namespace.vue +0 -3
  110. package/detail/node.vue +17 -13
  111. package/detail/provisioning.cattle.io.cluster.vue +72 -6
  112. package/dialog/AddCustomBadgeDialog.vue +1 -1
  113. package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
  114. package/{components/AssignTo.vue → dialog/AssignToDialog.vue} +71 -80
  115. package/dialog/ChangePasswordDialog.vue +106 -0
  116. package/dialog/DeactivateDriverDialog.vue +1 -0
  117. package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
  118. package/dialog/DisableAuthProviderDialog.vue +101 -0
  119. package/dialog/DrainNode.vue +1 -1
  120. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
  121. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +69 -57
  122. package/dialog/FeatureFlagListDialog.vue +288 -0
  123. package/dialog/ForceMachineRemoveDialog.vue +5 -2
  124. package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
  125. package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
  126. package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -59
  127. package/dialog/MoveNamespaceDialog.vue +157 -0
  128. package/dialog/ScalePoolDownDialog.vue +1 -1
  129. package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
  130. package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
  131. package/dialog/WechatDialog.vue +57 -0
  132. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
  133. package/edit/auth/__tests__/oidc.test.ts +152 -109
  134. package/edit/auth/azuread.vue +2 -1
  135. package/edit/auth/github.vue +1 -1
  136. package/edit/auth/googleoauth.vue +5 -1
  137. package/edit/auth/ldap/index.vue +1 -1
  138. package/edit/auth/oidc.vue +38 -5
  139. package/edit/auth/saml.vue +1 -1
  140. package/edit/cloudcredential.vue +24 -9
  141. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
  142. package/edit/management.cattle.io.user.vue +28 -3
  143. package/edit/namespace.vue +1 -4
  144. package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
  145. package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
  146. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
  147. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
  148. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
  149. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
  150. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
  151. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
  152. package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
  153. package/edit/provisioning.cattle.io.cluster/rke2.vue +49 -41
  154. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
  155. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
  156. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
  157. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
  158. package/edit/token.vue +2 -0
  159. package/edit/workload/index.vue +1 -0
  160. package/edit/workload/mixins/workload.js +0 -2
  161. package/initialize/install-plugins.js +2 -1
  162. package/list/harvesterhci.io.management.cluster.vue +4 -1
  163. package/list/management.cattle.io.feature.vue +4 -287
  164. package/list/provisioning.cattle.io.cluster.vue +20 -12
  165. package/machine-config/azure.vue +16 -4
  166. package/mixins/vue-select-overrides.js +0 -4
  167. package/models/__tests__/namespace.test.ts +25 -1
  168. package/models/cloudcredential.js +5 -0
  169. package/models/fleet.cattle.io.cluster.js +8 -2
  170. package/models/fleet.cattle.io.gitrepo.js +8 -34
  171. package/models/kontainerdriver.js +6 -3
  172. package/models/management.cattle.io.feature.js +7 -1
  173. package/models/management.cattle.io.node.js +3 -3
  174. package/models/namespace.js +11 -6
  175. package/models/nodedriver.js +6 -3
  176. package/models/workload.js +4 -1
  177. package/package.json +3 -3
  178. package/pages/about.vue +13 -3
  179. package/pages/account/index.vue +16 -6
  180. package/pages/auth/login.vue +18 -7
  181. package/pages/auth/logout.vue +4 -1
  182. package/pages/auth/setup.vue +2 -0
  183. package/pages/auth/verify.vue +13 -8
  184. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  185. package/pages/c/_cluster/apps/charts/install.vue +26 -26
  186. package/pages/c/_cluster/auth/config/index.vue +10 -12
  187. package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
  188. package/pages/c/_cluster/explorer/index.vue +17 -15
  189. package/pages/c/_cluster/istio/index.vue +2 -2
  190. package/pages/c/_cluster/longhorn/index.vue +1 -1
  191. package/pages/c/_cluster/monitoring/index.vue +1 -1
  192. package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
  193. package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
  194. package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
  195. package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
  196. package/pages/c/_cluster/neuvector/index.vue +1 -1
  197. package/pages/c/_cluster/settings/banners.vue +4 -3
  198. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
  199. package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
  200. package/pages/c/_cluster/uiplugins/index.vue +98 -55
  201. package/pages/diagnostic.vue +59 -11
  202. package/pages/fail-whale.vue +14 -8
  203. package/pages/home.vue +24 -18
  204. package/pages/prefs.vue +7 -6
  205. package/pages/support/index.vue +4 -1
  206. package/plugins/internal-api/index.ts +37 -0
  207. package/plugins/internal-api/shared/base-api.ts +13 -0
  208. package/plugins/internal-api/shell/shell.api.ts +108 -0
  209. package/plugins/steve/actions.js +0 -12
  210. package/public/index.html +1 -0
  211. package/rancher-components/Card/Card.vue +1 -1
  212. package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
  213. package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
  214. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +47 -0
  215. package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
  216. package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
  217. package/rancher-components/Form/Radio/RadioButton.vue +20 -4
  218. package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
  219. package/rancher-components/Form/Radio/RadioGroup.vue +52 -10
  220. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
  221. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +5 -0
  222. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
  223. package/rancher-components/RcButton/RcButton.vue +2 -1
  224. package/rancher-components/RcButton/types.ts +1 -0
  225. package/rancher-components/RcDropdown/RcDropdown.vue +18 -6
  226. package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
  227. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
  228. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
  229. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
  230. package/rancher-components/RcDropdown/index.ts +2 -0
  231. package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
  232. package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
  233. package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
  234. package/scripts/extension/bundle +20 -0
  235. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
  236. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  237. package/scripts/extension/helmpatch +44 -31
  238. package/scripts/extension/publish +12 -12
  239. package/scripts/typegen.sh +2 -4
  240. package/server/har-file.js +25 -3
  241. package/store/action-menu.js +26 -56
  242. package/store/features.js +2 -1
  243. package/store/index.js +5 -0
  244. package/store/modal.ts +71 -0
  245. package/store/slideInPanel.ts +47 -0
  246. package/store/type-map.js +12 -1
  247. package/store/type-map.utils.ts +4 -4
  248. package/types/global-vue.d.ts +5 -0
  249. package/types/internal-api/shell/growl.d.ts +25 -0
  250. package/types/internal-api/shell/modal.d.ts +77 -0
  251. package/types/internal-api/shell/slideIn.d.ts +15 -0
  252. package/types/resources/fleet.d.ts +0 -14
  253. package/types/shell/index.d.ts +43 -24
  254. package/types/vue-shim.d.ts +4 -1
  255. package/utils/__mocks__/tabbable.js +13 -0
  256. package/utils/__tests__/object.test.ts +38 -4
  257. package/utils/cluster.js +35 -0
  258. package/utils/fleet.ts +15 -73
  259. package/utils/object.js +48 -5
  260. package/utils/validators/formRules/__tests__/index.test.ts +10 -1
  261. package/utils/validators/formRules/index.ts +27 -3
  262. package/utils/validators/machine-pool.ts +20 -0
  263. package/components/DisableAuthProviderModal.vue +0 -114
  264. package/components/MoveModal.vue +0 -166
  265. package/components/PromptChangePassword.vue +0 -123
  266. package/components/fleet/FleetBundleResources.vue +0 -86
  267. package/components/formatter/ExtensionCache.vue +0 -74
  268. package/components/formatter/Port.vue +0 -24
  269. package/components/formatter/SecretType.vue +0 -41
  270. package/types/vue-shim.d +0 -20
@@ -363,7 +363,7 @@ export default {
363
363
  </template>
364
364
  </AuthBanner>
365
365
 
366
- <hr>
366
+ <hr role="none">
367
367
 
368
368
  <AllowedPrincipals
369
369
  provider="azuread"
@@ -388,6 +388,7 @@ export default {
388
388
  <label class="reply-url">{{ t('authConfig.azuread.reply.label') }} </label>
389
389
  <CopyToClipboardText
390
390
  :plain="true"
391
+ :aria-label="t('authConfig.azuread.reply.ariaLabel')"
391
392
  :text="replyUrl"
392
393
  />
393
394
  </InfoBox>
@@ -143,7 +143,7 @@ export default {
143
143
  </template>
144
144
  </AuthBanner>
145
145
 
146
- <hr>
146
+ <hr role="none">
147
147
 
148
148
  <AllowedPrincipals
149
149
  provider="github"
@@ -90,7 +90,7 @@ export default {
90
90
  </template>
91
91
  </AuthBanner>
92
92
 
93
- <hr>
93
+ <hr role="none">
94
94
 
95
95
  <AllowedPrincipals
96
96
  provider="googleoauth"
@@ -143,12 +143,14 @@ export default {
143
143
  <b>{{ t('authConfig.googleoauth.steps.1.body.2', {}, true) }}</b> {{ t('authConfig.googleoauth.steps.1.topPrivateDomain', {}, true) }} <CopyToClipboardText
144
144
  :plain="true"
145
145
  :text="tArgs.hostname"
146
+ :aria-label="t('authConfig.googleoauth.steps.1.ariaLabel.hostname')"
146
147
  />
147
148
  </li>
148
149
  <li>
149
150
  <b>{{ t('authConfig.googleoauth.steps.1.body.3', {}, true) }}</b> <CopyToClipboardText
150
151
  :plain="true"
151
152
  :text="serverUrl"
153
+ :aria-label="t('authConfig.googleoauth.steps.1.ariaLabel.serverUrl')"
152
154
  />
153
155
  </li>
154
156
  <li>{{ t('authConfig.googleoauth.steps.1.body.4', {}, true) }} </li>
@@ -170,12 +172,14 @@ export default {
170
172
  <b>{{ t('authConfig.googleoauth.steps.2.body.2', {}, true) }}</b> <CopyToClipboardText
171
173
  :plain="true"
172
174
  :text="serverUrl"
175
+ :aria-label="t('authConfig.googleoauth.steps.1.ariaLabel.serverUrl')"
173
176
  />
174
177
  </li>
175
178
  <li>
176
179
  <b>{{ t('authConfig.googleoauth.steps.2.body.3', {}, true) }}</b> <CopyToClipboardText
177
180
  :plain="true"
178
181
  :text="serverUrl+'/verify-auth'"
182
+ :aria-label="t('authConfig.googleoauth.steps.2.ariaLabel.serverUrlVerify')"
179
183
  />
180
184
  </li>
181
185
  <li>{{ t('authConfig.googleoauth.steps.2.body.4', {}, true) }} </li>
@@ -99,7 +99,7 @@ export default {
99
99
  </template>
100
100
  </AuthBanner>
101
101
 
102
- <hr>
102
+ <hr role="none">
103
103
 
104
104
  <AllowedPrincipals
105
105
  :provider="NAME"
@@ -12,6 +12,7 @@ import ArrayList from '@shell/components/form/ArrayList';
12
12
  import { LabeledInput } from '@components/Form/LabeledInput';
13
13
  import { RadioGroup } from '@components/Form/Radio';
14
14
  import { Checkbox } from '@components/Form/Checkbox';
15
+ import { BASE_SCOPES } from '@shell/store/auth';
15
16
 
16
17
  export default {
17
18
  components: {
@@ -50,6 +51,7 @@ export default {
50
51
  tokenEndpoint: null,
51
52
  userInfoEndpoint: null,
52
53
  },
54
+ // TODO #13457: this is duplicated due wrong format
53
55
  oidcScope: []
54
56
  };
55
57
  },
@@ -76,9 +78,10 @@ export default {
76
78
  }
77
79
 
78
80
  const { clientId, clientSecret } = this.model;
79
- const isValidScope = this.model.id === 'keycloakoidc' || this.oidcScope?.includes('openid');
81
+ const isMissingAuthEndpoint = (this.requiresAuthEndpoint && !this.model.authEndpoint);
82
+ const isMissingScopes = !this.requiredScopes.every((scope) => this.oidcScope.includes(scope));
80
83
 
81
- if ( !isValidScope ) {
84
+ if (isMissingAuthEndpoint || isMissingScopes) {
82
85
  return false;
83
86
  }
84
87
 
@@ -91,6 +94,19 @@ export default {
91
94
 
92
95
  return !!(clientId && clientSecret && rancherUrl && issuer);
93
96
  }
97
+ },
98
+
99
+ requiresAuthEndpoint() {
100
+ return ['genericoidc', 'keycloakoidc'].includes(this.model.id);
101
+ },
102
+
103
+ /**
104
+ * TODO #13457: Refactor scopes to be an array of terms
105
+ * Return valid scopes
106
+ * The scopes for given auth provider (model.id) have format of ['scope1 scope2 scope3']
107
+ */
108
+ requiredScopes() {
109
+ return this.model.id ? (BASE_SCOPES[this.model.id] || []) ? (BASE_SCOPES[this.model.id] || [])[0].split(' ') : [] : [];
94
110
  }
95
111
  },
96
112
 
@@ -104,6 +120,7 @@ export default {
104
120
  },
105
121
 
106
122
  'model.enabled'(neu) {
123
+ // TODO #13457: Refactor scopes to be an array of terms
107
124
  // Cover case where oidc gets disabled and we return to the edit screen with a reset model
108
125
  if (!neu) {
109
126
  this.oidcUrls = {
@@ -114,8 +131,10 @@ export default {
114
131
  userInfoEndpoint: null,
115
132
  };
116
133
  this.customEndpoint.value = false;
134
+ // TODO #13457: Refactor scopes to be an array of terms
117
135
  this.oidcScope = this.model?.scope?.split(' ');
118
136
  } else {
137
+ // TODO #13457: Refactor scopes to be an array of terms
119
138
  this.oidcScope = this.model?.scope?.split(' ');
120
139
  }
121
140
  },
@@ -130,10 +149,16 @@ export default {
130
149
 
131
150
  methods: {
132
151
  updateEndpoints() {
152
+ const isKeycloak = this.model.id === 'keycloakoidc';
153
+
133
154
  if (!this.oidcUrls.url) {
155
+ this.model.issuer = '';
156
+ if (isKeycloak) {
157
+ this.model.authEndpoint = '';
158
+ }
159
+
134
160
  return;
135
161
  }
136
- const isKeycloak = this.model.id === 'keycloakoidc';
137
162
 
138
163
  const url = this.oidcUrls.url.replaceAll(' ', '');
139
164
  const realmsPath = 'realms';
@@ -184,7 +209,7 @@ export default {
184
209
  </template>
185
210
  </AuthBanner>
186
211
 
187
- <hr>
212
+ <hr role="none">
188
213
 
189
214
  <AllowedPrincipals
190
215
  :provider="NAME"
@@ -201,6 +226,7 @@ export default {
201
226
 
202
227
  <h3>{{ t(`authConfig.oidc.${NAME}`) }}</h3>
203
228
 
229
+ <!-- Auth credentials -->
204
230
  <div class="row mb-20">
205
231
  <div class="col span-6">
206
232
  <LabeledInput
@@ -222,6 +248,7 @@ export default {
222
248
  </div>
223
249
  </div>
224
250
 
251
+ <!-- Key/Certificate -->
225
252
  <div class="row mb-20">
226
253
  <div class="col span-6">
227
254
  <LabeledInput
@@ -255,6 +282,7 @@ export default {
255
282
  </div>
256
283
  </div>
257
284
 
285
+ <!-- Allow group search -->
258
286
  <div class="row mb-20">
259
287
  <div class="col span-6">
260
288
  <Checkbox
@@ -267,6 +295,7 @@ export default {
267
295
  </div>
268
296
  </div>
269
297
 
298
+ <!-- Scopes -->
270
299
  <div class="row mb-20">
271
300
  <div class="col span-6">
272
301
  <ArrayList
@@ -280,6 +309,7 @@ export default {
280
309
  </div>
281
310
  </div>
282
311
 
312
+ <!-- Generated vs Specific Endpoints -->
283
313
  <div class="row mb-20">
284
314
  <div class="col span-6">
285
315
  <RadioGroup
@@ -297,6 +327,7 @@ export default {
297
327
  </div>
298
328
  </div>
299
329
 
330
+ <!-- Generated endpoints -->
300
331
  <div class="row mb-20">
301
332
  <div class="col span-6">
302
333
  <LabeledInput
@@ -320,6 +351,7 @@ export default {
320
351
  </div>
321
352
  </div>
322
353
 
354
+ <!-- Specific Endpoints -->
323
355
  <div class="row mb-20">
324
356
  <div class="col span-6">
325
357
  <LabeledInput
@@ -350,12 +382,13 @@ export default {
350
382
  :label="t(`authConfig.oidc.authEndpoint`)"
351
383
  :mode="mode"
352
384
  :disabled="!customEndpoint.value"
353
- :required="model.id === 'keycloakoidc'"
385
+ :required="requiresAuthEndpoint"
354
386
  data-testid="oidc-auth-endpoint"
355
387
  />
356
388
  </div>
357
389
  </div>
358
390
 
391
+ <!-- Advanced section -->
359
392
  <AdvancedSection :mode="mode">
360
393
  <div class="row mb-20">
361
394
  <div class="col span-6">
@@ -223,7 +223,7 @@ export default {
223
223
  </template>
224
224
  </AuthBanner>
225
225
 
226
- <hr>
226
+ <hr role="none">
227
227
 
228
228
  <AllowedPrincipals
229
229
  :provider="NAME"
@@ -100,6 +100,30 @@ export default {
100
100
  computed: {
101
101
  rke2Enabled: mapFeature(RKE2_FEATURE),
102
102
 
103
+ hasCustomCloudCredentialComponent() {
104
+ const driverName = this.driverName;
105
+
106
+ return this.$store.getters['type-map/hasCustomCloudCredentialComponent'](driverName);
107
+ },
108
+
109
+ cloudCredentialComponent() {
110
+ const driverName = this.driverName;
111
+
112
+ return this.$store.getters['type-map/importCloudCredential'](driverName);
113
+ },
114
+
115
+ genericCloudCredentialComponent() {
116
+ return this.$store.getters['type-map/importCloudCredential']('generic');
117
+ },
118
+
119
+ cloudComponent() {
120
+ if (this.hasCustomCloudCredentialComponent) {
121
+ return this.cloudCredentialComponent;
122
+ }
123
+
124
+ return this.genericCloudCredentialComponent;
125
+ },
126
+
103
127
  validationPassed() {
104
128
  return this.credCustomComponentValidation && this.nameRequiredValidation;
105
129
  },
@@ -112,14 +136,6 @@ export default {
112
136
  return this.value?.provider;
113
137
  },
114
138
 
115
- cloudComponent() {
116
- if (this.$store.getters['type-map/hasCustomCloudCredentialComponent'](this.driverName)) {
117
- return this.$store.getters['type-map/importCloudCredential'](this.driverName);
118
- }
119
-
120
- return this.$store.getters['type-map/importCloudCredential']('generic');
121
- },
122
-
123
139
  // array of id, label, description, initials for type selection step
124
140
  secretSubTypes() {
125
141
  const out = [];
@@ -194,7 +210,6 @@ export default {
194
210
  },
195
211
 
196
212
  methods: {
197
-
198
213
  createValidationChanged(passed) {
199
214
  this.credCustomComponentValidation = passed;
200
215
  },
@@ -1,5 +1,6 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import Banzai from '@shell/edit/logging.banzaicloud.io.output/index.vue';
3
+ import { createStore } from 'vuex';
3
4
 
4
5
  const outputSchema = {
5
6
  id: 'logging.banzaicloud.io.output',
@@ -109,16 +110,26 @@ describe('view: logging.banzaicloud.io.output', () => {
109
110
  ['http://localhost:3100', []],
110
111
  ['not a proper URL', ['logging.loki.urlInvalid']],
111
112
  ])('should validate Loki URL on save', (url, expectation) => {
113
+ const store = createStore({
114
+ getters: {
115
+ namespaces: () => () => ({}),
116
+ currentStore: () => () => 'cluster',
117
+ 'cluster/schemaFor': () => jest.fn()
118
+ }
119
+ });
112
120
  const wrapper = mount(Banzai, {
113
121
  data: () => ({ selectedProvider: 'loki' }),
114
122
  props: {
115
123
  value: {
116
- save: jest.fn(),
117
- spec: { loki: { url } }
124
+ save: jest.fn(),
125
+ setAnnotation: jest.fn(),
126
+ spec: { loki: { url } },
127
+ metadata: {},
118
128
  }
119
129
  },
120
130
  global: {
121
- mocks: {
131
+ provide: { store },
132
+ mocks: {
122
133
  $fetchState: { pending: false },
123
134
  $store: {
124
135
  dispatch: jest.fn(),
@@ -149,16 +160,26 @@ describe('view: logging.banzaicloud.io.output', () => {
149
160
  });
150
161
 
151
162
  it('should load the default YAML data for output buffer config (from schema) in a CREATE scenario', async() => {
163
+ const store = createStore({
164
+ getters: {
165
+ namespaces: () => () => ({}),
166
+ currentStore: () => () => 'cluster',
167
+ 'cluster/schemaFor': () => jest.fn()
168
+ }
169
+ });
152
170
  const wrapper = mount(Banzai, {
153
171
  data: () => ({ selectedProvider: 'awsElasticsearch' }),
154
172
  props: {
155
173
  value: {
156
- save: jest.fn(),
157
- spec: {}
174
+ save: jest.fn(),
175
+ setAnnotation: jest.fn(),
176
+ spec: {},
177
+ metadata: {},
158
178
  }
159
179
  },
160
180
  global: {
161
- mocks: {
181
+ provide: { store },
182
+ mocks: {
162
183
  $fetchState: { pending: false },
163
184
  $store: {
164
185
  dispatch(arg: any) {
@@ -230,16 +251,26 @@ describe('view: logging.banzaicloud.io.output', () => {
230
251
  });
231
252
 
232
253
  it('should load current output buffer config in an EDIT scenario', async() => {
254
+ const store = createStore({
255
+ getters: {
256
+ namespaces: () => () => ({}),
257
+ currentStore: () => () => 'cluster',
258
+ 'cluster/schemaFor': () => jest.fn()
259
+ }
260
+ });
233
261
  const wrapper = mount(Banzai, {
234
262
  data: () => ({ selectedProvider: 'awsElasticsearch' }),
235
263
  props: {
236
264
  value: {
237
- save: jest.fn(),
238
- spec: { awsElasticsearch: { buffer: '#chunk_limit_records: int' } }
265
+ save: jest.fn(),
266
+ setAnnotation: jest.fn(),
267
+ spec: { awsElasticsearch: { buffer: '#chunk_limit_records: int' } },
268
+ metadata: {}
239
269
  }
240
270
  },
241
271
  global: {
242
- mocks: {
272
+ provide: { store },
273
+ mocks: {
243
274
  $fetchState: { pending: false },
244
275
  $store: {
245
276
  dispatch(arg: any) {
@@ -15,6 +15,8 @@ export default {
15
15
  ChangePassword, GlobalRoleBindings, CruResource, LabeledInput, Loading
16
16
  },
17
17
 
18
+ emits: ['update:mode'],
19
+
18
20
  mixins: [
19
21
  CreateEditView
20
22
  ],
@@ -40,6 +42,7 @@ export default {
40
42
  roles: !showGlobalRoles,
41
43
  rolesChanged: false,
42
44
  },
45
+ watchOverride: false,
43
46
  };
44
47
  },
45
48
 
@@ -115,7 +118,11 @@ export default {
115
118
  this.$router.replace({ name: this.doneRoute });
116
119
  buttonDone(true);
117
120
  } catch (err) {
118
- this.errors = exceptionToErrorsArray(err);
121
+ if (err?.message?.includes('errors due to escalation')) {
122
+ this.errors = [this.t('rbac.errors.escalation')];
123
+ } else {
124
+ this.errors = exceptionToErrorsArray(err);
125
+ }
119
126
  buttonDone(false);
120
127
  }
121
128
  },
@@ -150,7 +157,7 @@ export default {
150
157
 
151
158
  const normanUser = await this.$store.dispatch('rancher/find', {
152
159
  type: NORMAN.USER,
153
- id: this.value.id,
160
+ id: this.value.id || this.user?.id,
154
161
  });
155
162
 
156
163
  // Save change of password
@@ -181,8 +188,25 @@ export default {
181
188
  },
182
189
 
183
190
  async updateRoles(userId) {
184
- if (this.$refs.grb) {
191
+ if (!this.$refs.grb) {
192
+ return;
193
+ }
194
+
195
+ try {
185
196
  await this.$refs.grb.save(userId);
197
+ } catch (err) {
198
+ if (this.isCreate) {
199
+ this.watchOverride = true;
200
+ this.$emit(
201
+ 'update:mode',
202
+ {
203
+ userId,
204
+ mode: _EDIT,
205
+ resource: 'management.cattle.io.user',
206
+ }
207
+ );
208
+ }
209
+ throw err;
186
210
  }
187
211
  }
188
212
  }
@@ -255,6 +279,7 @@ export default {
255
279
  :user-id="value.id || liveValue.id"
256
280
  :mode="mode"
257
281
  :real-mode="realMode"
282
+ :watch-override="watchOverride"
258
283
  type="user"
259
284
  @hasChanges="validation.rolesChanged = $event"
260
285
  @canLogIn="validation.roles = $event"
@@ -11,7 +11,6 @@ import Tab from '@shell/components/Tabbed/Tab';
11
11
  import ResourceTabs from '@shell/components/form/ResourceTabs/index.vue';
12
12
  import CruResource from '@shell/components/CruResource';
13
13
  import { PROJECT_ID, _VIEW, FLAT_VIEW, _CREATE } from '@shell/config/query-params';
14
- import MoveModal from '@shell/components/MoveModal';
15
14
  import ResourceQuota from '@shell/components/form/ResourceQuota/Namespace';
16
15
  import Loading from '@shell/components/Loading';
17
16
  import { HARVESTER_TYPES, RANCHER_TYPES } from '@shell/components/form/ResourceQuota/shared';
@@ -31,8 +30,7 @@ export default {
31
30
  PodSecurityAdmission,
32
31
  ResourceQuota,
33
32
  Tab,
34
- ResourceTabs,
35
- MoveModal
33
+ ResourceTabs
36
34
  },
37
35
 
38
36
  mixins: [CreateEditView],
@@ -277,6 +275,5 @@ export default {
277
275
  />
278
276
  </Tab>
279
277
  </ResourceTabs>
280
- <MoveModal v-if="projects" />
281
278
  </CruResource>
282
279
  </template>
@@ -5,6 +5,7 @@ import { get, set, remove } from '@shell/utils/object';
5
5
 
6
6
  export default {
7
7
  components: { LabeledSelect },
8
+ emits: ['update:value'],
8
9
  props: {
9
10
  value: {
10
11
  type: Object,
@@ -37,13 +38,16 @@ export default {
37
38
  },
38
39
  methods: {
39
40
  update(e) {
40
- if (!e || e.label === this.t('generic.none')) {
41
+ if (!e || e === '' || e.label === this.t('generic.none')) {
41
42
  remove(this.value, 'spec.ingressClassName');
42
43
  this.ingressClassName = '';
44
+ this.$emit('update:value', this.value);
43
45
  } else {
44
46
  // when a user manually types an ingress class name, the event emitted has a 'label' but no 'value'
45
- this.ingressClassName = e.value ? e.value : e.label;
47
+ this.ingressClassName = e.label || e;
46
48
  set(this.value, 'spec.ingressClassName', this.ingressClassName);
49
+
50
+ this.$emit('update:value', this.value);
47
51
  }
48
52
  }
49
53
  }
@@ -59,7 +63,7 @@ export default {
59
63
  :label="t('ingress.ingressClass.label')"
60
64
  :options="ingressClassOptions"
61
65
  option-label="label"
62
- @selecting="update"
66
+ @update:value="update"
63
67
  />
64
68
  </div>
65
69
  </template>
@@ -0,0 +1,58 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import IngressClass from '@shell/edit/networking.k8s.io.ingress/IngressClass.vue';
3
+ import LabeledSelect from '@shell/components/form/LabeledSelect';
4
+ import { _EDIT } from '@shell/config/query-params';
5
+
6
+ jest.mock('@shell/components/form/LabeledSelect', () => ({
7
+ name: 'LabeledSelect',
8
+ template: '<div></div>',
9
+ props: ['value', 'taggable', 'searchable', 'mode', 'label', 'options', 'optionLabel'],
10
+ emits: ['update:value'],
11
+ }));
12
+
13
+ describe('ingressClass.vue', () => {
14
+ it('renders correctly', () => {
15
+ const wrapper = shallowMount(IngressClass, {
16
+ props: {
17
+ value: {},
18
+ ingressClasses: [],
19
+ mode: _EDIT,
20
+ },
21
+ });
22
+
23
+ expect(wrapper.exists()).toBe(true);
24
+ expect(wrapper.findComponent(LabeledSelect).exists()).toBe(true);
25
+ });
26
+
27
+ it('updates ingressClassName correctly', async() => {
28
+ const wrapper = shallowMount(IngressClass, {
29
+ props: {
30
+ value: {},
31
+ ingressClasses: [{ label: 'nginx', value: 'nginx' }],
32
+ mode: _EDIT,
33
+ },
34
+ });
35
+
36
+ await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'nginx');
37
+
38
+ expect(wrapper.vm.ingressClassName).toBe('nginx');
39
+ expect(wrapper.props('value').spec.ingressClassName).toBe('nginx');
40
+ expect(wrapper.emitted()['update:value']).toBeTruthy();
41
+ });
42
+
43
+ it('removes ingressClassName when none is selected', async() => {
44
+ const wrapper = shallowMount(IngressClass, {
45
+ props: {
46
+ value: { spec: { ingressClassName: 'nginx' } },
47
+ ingressClasses: [{ label: 'nginx', value: 'nginx' }],
48
+ mode: _EDIT,
49
+ },
50
+ });
51
+
52
+ await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', '');
53
+
54
+ expect(wrapper.vm.ingressClassName).toBe('');
55
+ expect(wrapper.props('value').spec.ingressClassName).toBeUndefined();
56
+ expect(wrapper.emitted()['update:value']).toBeTruthy();
57
+ });
58
+ });
@@ -9,7 +9,13 @@ describe('view: PersistentVolume', () => {
9
9
  };
10
10
  const resource = 'PersistentVolume';
11
11
  const wrapper = mount(PersistentVolume as ExtendedVue<Vue, {}, {}, {}, PersistentVolume>, {
12
- props: { value: { spec: { } } },
12
+ props: {
13
+ value: {
14
+ setAnnotation: jest.fn(),
15
+ spec: {},
16
+ metadata: {},
17
+ }
18
+ },
13
19
 
14
20
  global: {
15
21
  mocks: {
@@ -54,7 +60,13 @@ describe('view: PersistentVolume', () => {
54
60
  const plugin = 'csi';
55
61
  const resource = 'PersistentVolume';
56
62
  const wrapper = mount(PersistentVolume as ExtendedVue<Vue, {}, {}, {}, PersistentVolume>, {
57
- props: { value: { spec: { [plugin]: { value: plugin } } } },
63
+ props: {
64
+ value: {
65
+ setAnnotation: jest.fn(),
66
+ spec: { [plugin]: { value: plugin } },
67
+ metadata: {},
68
+ }
69
+ },
58
70
  global: {
59
71
  mocks: {
60
72
  $store: {
@@ -260,7 +260,10 @@ export default {
260
260
  />
261
261
 
262
262
  <template v-if="cluster.supportsWindows">
263
- <hr class="mt-20 mb-20">
263
+ <hr
264
+ class="mt-20 mb-20"
265
+ role="none"
266
+ >
264
267
  <h4 v-t="'cluster.custom.registrationCommand.windowsDetail'" />
265
268
  <Banner
266
269
  v-if="readyForWindows"