@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -0,0 +1,206 @@
1
+ import { nextTick } from 'vue';
2
+ import { mount, type VueWrapper, flushPromises } from '@vue/test-utils';
3
+ import { _EDIT } from '@shell/config/query-params';
4
+
5
+ import LDAPIndex from '@shell/edit/auth/ldap/index.vue';
6
+
7
+ jest.mock('@shell/utils/clipboard', () => {
8
+ return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
9
+ });
10
+
11
+ const validOpenLdapModel = {
12
+ enabled: false,
13
+ servers: ['ldap.example.com'],
14
+ port: 389,
15
+ connectionTimeout: 5000,
16
+ serviceAccountDistinguishedName: 'cn=admin,dc=example,dc=com',
17
+ serviceAccountPassword: 'secretpassword',
18
+ userSearchBase: 'dc=example,dc=com',
19
+ };
20
+
21
+ const validActiveDirectoryModel = {
22
+ enabled: false,
23
+ servers: ['ad.example.com'],
24
+ port: 389,
25
+ connectionTimeout: 5000,
26
+ serviceAccountUsername: 'DOMAIN\\admin',
27
+ serviceAccountPassword: 'secretpassword',
28
+ userSearchBase: 'dc=example,dc=com',
29
+ };
30
+
31
+ const validUsername = 'testuser';
32
+ const validPassword = 'testpassword';
33
+
34
+ const buildSetup = (type = 'openldap', modelOverride = {}, localDataOverride = {}) => ({
35
+ data() {
36
+ const baseModel = type === 'activedirectory' ? validActiveDirectoryModel : validOpenLdapModel;
37
+
38
+ return {
39
+ isEnabling: false,
40
+ editConfig: false,
41
+ model: { ...baseModel, ...modelOverride },
42
+ username: validUsername,
43
+ password: validPassword,
44
+ errors: [],
45
+ serverSetting: null,
46
+ originalModel: null,
47
+ principals: [],
48
+ authConfigName: type,
49
+ ...localDataOverride,
50
+ } as any;
51
+ },
52
+ global: {
53
+ mocks: {
54
+ $fetchState: { pending: false },
55
+ $store: {
56
+ getters: {
57
+ currentStore: () => 'current_store',
58
+ 'current_store/schemaFor': jest.fn(),
59
+ 'current_store/all': jest.fn(),
60
+ 'i18n/t': (val: string) => val,
61
+ 'i18n/exists': jest.fn(),
62
+ },
63
+ dispatch: jest.fn()
64
+ },
65
+ $route: { query: { AS: '' }, params: { id: type } },
66
+ $router: { applyQuery: jest.fn() },
67
+ },
68
+ },
69
+ props: {
70
+ value: { ...validOpenLdapModel, ...modelOverride },
71
+ mode: _EDIT,
72
+ },
73
+ });
74
+
75
+ const mountAndValidate = async(type: string, modelOverride = {}, localDataOverride = {}) => {
76
+ const wrapper = mount(LDAPIndex, buildSetup(type, modelOverride, localDataOverride));
77
+
78
+ await wrapper.vm.validateAllFields();
79
+ await flushPromises();
80
+
81
+ return wrapper;
82
+ };
83
+
84
+ describe('ldap/index.vue', () => {
85
+ describe('given default valid values (openldap)', () => {
86
+ let wrapper: VueWrapper<any, any>;
87
+
88
+ beforeEach(() => {
89
+ wrapper = mount(LDAPIndex, buildSetup());
90
+ });
91
+
92
+ afterEach(() => {
93
+ wrapper.unmount();
94
+ });
95
+
96
+ it('has "Enable" button enabled when all required fields are filled', async() => {
97
+ await nextTick();
98
+
99
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
100
+
101
+ expect(saveButton.disabled).toBe(false);
102
+ });
103
+
104
+ it('has "Enable" button enabled when provider is already enabled and not editing config', async() => {
105
+ wrapper.setData({ model: { ...validOpenLdapModel, enabled: true }, editConfig: false });
106
+ await nextTick();
107
+
108
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
109
+
110
+ expect(saveButton.disabled).toBe(false);
111
+ });
112
+ });
113
+
114
+ describe('have "Enable" button disabled when required fields are empty', () => {
115
+ it.each([
116
+ ['username', { username: '', password: validPassword }],
117
+ ['password', { username: validUsername, password: '' }],
118
+ ])('given empty %s (test credentials)', async(field, localData) => {
119
+ const wrapper = await mountAndValidate('openldap', {}, localData);
120
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
121
+
122
+ expect(saveButton.disabled).toBe(true);
123
+ wrapper.unmount();
124
+ });
125
+
126
+ it.each([
127
+ ['serviceAccountDistinguishedName', { serviceAccountDistinguishedName: '' }],
128
+ ['serviceAccountPassword', { serviceAccountPassword: '' }],
129
+ ['userSearchBase', { userSearchBase: '' }],
130
+ ])('given empty %s (config field)', async(field, modelOverride) => {
131
+ const wrapper = await mountAndValidate('openldap', modelOverride);
132
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
133
+
134
+ expect(saveButton.disabled).toBe(true);
135
+ wrapper.unmount();
136
+ });
137
+ });
138
+
139
+ describe('certificate conditional validation', () => {
140
+ it('is not required when TLS and STARTTLS are disabled', async() => {
141
+ const wrapper = await mountAndValidate('openldap', { tls: false, starttls: false });
142
+
143
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
144
+
145
+ expect(saveButton.disabled).toBe(false);
146
+ wrapper.unmount();
147
+ });
148
+
149
+ it('is required and causes button disabled when TLS is enabled and certificate is empty', async() => {
150
+ const wrapper = await mountAndValidate('openldap', {
151
+ tls: true,
152
+ starttls: false,
153
+ certificate: '',
154
+ });
155
+
156
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
157
+
158
+ expect(saveButton.disabled).toBe(true);
159
+ wrapper.unmount();
160
+ });
161
+
162
+ it('button is enabled when TLS is enabled and certificate is provided', async() => {
163
+ const wrapper = mount(LDAPIndex, buildSetup('openldap', {
164
+ tls: true,
165
+ starttls: false,
166
+ certificate: '-----BEGIN CERTIFICATE-----\nMIIB\n-----END CERTIFICATE-----',
167
+ }));
168
+
169
+ await nextTick();
170
+
171
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
172
+
173
+ expect(saveButton.disabled).toBe(false);
174
+ wrapper.unmount();
175
+ });
176
+ });
177
+
178
+ describe('service account field conditional validation', () => {
179
+ it('requires serviceAccountDistinguishedName for openldap when empty', async() => {
180
+ const wrapper = await mountAndValidate('openldap', { serviceAccountDistinguishedName: '' });
181
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
182
+
183
+ expect(saveButton.disabled).toBe(true);
184
+ wrapper.unmount();
185
+ });
186
+
187
+ it('requires serviceAccountUsername for activedirectory when empty', async() => {
188
+ const wrapper = await mountAndValidate('activedirectory', { serviceAccountUsername: '' });
189
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
190
+
191
+ expect(saveButton.disabled).toBe(true);
192
+ wrapper.unmount();
193
+ });
194
+
195
+ it('button is enabled for activedirectory when serviceAccountUsername is provided', async() => {
196
+ const wrapper = mount(LDAPIndex, buildSetup('activedirectory'));
197
+
198
+ await nextTick();
199
+
200
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
201
+
202
+ expect(saveButton.disabled).toBe(false);
203
+ wrapper.unmount();
204
+ });
205
+ });
206
+ });
@@ -110,6 +110,7 @@ export default {
110
110
  <div class="col span-6">
111
111
  <LabeledInput
112
112
  v-model:value="hostname"
113
+ name="hostname"
113
114
  required
114
115
  :mode="mode"
115
116
  :hoover-tooltip="true"
@@ -121,6 +122,7 @@ export default {
121
122
  <div class="col span-4">
122
123
  <LabeledInput
123
124
  :value="model.port"
125
+ name="port"
124
126
  type="number"
125
127
  required
126
128
  :min="0"
@@ -156,6 +158,7 @@ export default {
156
158
  <div class="col span-12">
157
159
  <LabeledInput
158
160
  v-model:value="model.certificate"
161
+ name="certificate"
159
162
  required
160
163
  type="multiline"
161
164
  :mode="mode"
@@ -173,6 +176,7 @@ export default {
173
176
  <div class="col span-6">
174
177
  <UnitInput
175
178
  v-model:value="model.connectionTimeout"
179
+ name="connectionTimeout"
176
180
  required
177
181
  :mode="mode"
178
182
  :label="t('authConfig.ldap.serverConnectionTimeout')"
@@ -191,6 +195,7 @@ export default {
191
195
  >
192
196
  <LabeledInput
193
197
  v-model:value="model.serviceAccountUsername"
198
+ name="serviceAccountUsername"
194
199
  required
195
200
  :mode="mode"
196
201
  :label="t('authConfig.ldap.serviceAccountDN')"
@@ -203,6 +208,7 @@ export default {
203
208
  >
204
209
  <LabeledInput
205
210
  v-model:value="model.serviceAccountDistinguishedName"
211
+ name="serviceAccountDistinguishedName"
206
212
  required
207
213
  :mode="mode"
208
214
  :label="t('authConfig.ldap.serviceAccountDN')"
@@ -211,6 +217,7 @@ export default {
211
217
  <div class="col span-6">
212
218
  <LabeledInput
213
219
  v-model:value="model.serviceAccountPassword"
220
+ name="serviceAccountPassword"
214
221
  required
215
222
  type="password"
216
223
  :mode="mode"
@@ -254,6 +261,7 @@ export default {
254
261
  <div class="col span-6">
255
262
  <LabeledInput
256
263
  v-model:value="model.userSearchBase"
264
+ name="userSearchBase"
257
265
  required
258
266
  :mode="mode"
259
267
  :label="t('authConfig.ldap.userSearchBase.label')"
@@ -1,4 +1,9 @@
1
1
  <script>
2
+ import { ref, computed, provide } from 'vue';
3
+ import { useStore } from 'vuex';
4
+ import { useForm } from 'vee-validate';
5
+ import { toTypedSchema } from '@vee-validate/zod';
6
+ import * as z from 'zod';
2
7
  import Loading from '@shell/components/Loading';
3
8
  import CreateEditView from '@shell/mixins/create-edit-view';
4
9
  import CruResource from '@shell/components/CruResource';
@@ -9,6 +14,7 @@ import AuthConfig from '@shell/mixins/auth-config';
9
14
  import AuthBanner from '@shell/components/auth/AuthBanner';
10
15
  import Password from '@shell/components/form/Password';
11
16
  import AuthProviderWarningBanners from '@shell/edit/auth/AuthProviderWarningBanners';
17
+ import { useI18n } from '@shell/composables/useI18n';
12
18
 
13
19
  const AUTH_TYPE = 'ldap';
14
20
 
@@ -26,6 +32,49 @@ export default {
26
32
 
27
33
  mixins: [CreateEditView, AuthConfig],
28
34
 
35
+ setup() {
36
+ const store = useStore();
37
+ const { t } = useI18n(store);
38
+
39
+ const coerce = (schema) => z.preprocess((v) => (v === null || v === undefined) ? '' : String(v), schema);
40
+ const requiredField = (key) => coerce(z.string().min(1, t('validation.required', { key: t(key) })));
41
+ const optionalField = coerce(z.string());
42
+
43
+ const tlsEnabledRef = ref(false);
44
+ const isActiveDirectoryRef = ref(false);
45
+
46
+ const validationSchema = computed(() => toTypedSchema(
47
+ z.object({
48
+ hostname: requiredField('authConfig.ldap.hostname.label'),
49
+ port: requiredField('authConfig.ldap.port'),
50
+ certificate: tlsEnabledRef.value ? requiredField('authConfig.ldap.cert') : optionalField,
51
+ connectionTimeout: requiredField('authConfig.ldap.serverConnectionTimeout'),
52
+ serviceAccountUsername: isActiveDirectoryRef.value ? requiredField('authConfig.ldap.serviceAccountDN') : optionalField,
53
+ serviceAccountDistinguishedName: !isActiveDirectoryRef.value ? requiredField('authConfig.ldap.serviceAccountDN') : optionalField,
54
+ serviceAccountPassword: requiredField('authConfig.ldap.serviceAccountPassword'),
55
+ userSearchBase: requiredField('authConfig.ldap.userSearchBase.label'),
56
+ username: requiredField(`authConfig.${ AUTH_TYPE }.username`),
57
+ password: requiredField(`authConfig.${ AUTH_TYPE }.password`),
58
+ })
59
+ ));
60
+
61
+ const showAllErrors = ref(false);
62
+
63
+ provide('vee-show-all-errors', showAllErrors);
64
+
65
+ const { errors, validate } = useForm({ validationSchema });
66
+ const isFormValid = computed(() => Object.keys(errors.value).length === 0);
67
+
68
+ const validateAllFields = async() => {
69
+ await validate();
70
+ showAllErrors.value = true;
71
+ };
72
+
73
+ return {
74
+ isFormValid, validateAllFields, tlsEnabledRef, isActiveDirectoryRef
75
+ };
76
+ },
77
+
29
78
  data() {
30
79
  return {
31
80
  username: null,
@@ -33,7 +82,21 @@ export default {
33
82
  };
34
83
  },
35
84
 
85
+ created() {
86
+ this.tlsEnabledRef = !!(this.model?.tls || this.model?.starttls);
87
+ this.isActiveDirectoryRef = this.NAME === 'activedirectory';
88
+ this.registerBeforeHook(this.validateAllFields, 'willSave');
89
+ },
90
+
36
91
  computed: {
92
+ validationPassed() {
93
+ if (this.model?.enabled && !this.editConfig) {
94
+ return true;
95
+ }
96
+
97
+ return this.isFormValid;
98
+ },
99
+
37
100
  tArgs() {
38
101
  return {
39
102
  provider: this.displayName,
@@ -66,6 +129,15 @@ export default {
66
129
  },
67
130
  },
68
131
 
132
+ watch: {
133
+ 'model.tls'(neu) {
134
+ this.tlsEnabledRef = !!(neu || this.model?.starttls);
135
+ },
136
+ 'model.starttls'(neu) {
137
+ this.tlsEnabledRef = !!(this.model?.tls || neu);
138
+ },
139
+ },
140
+
69
141
  };
70
142
  </script>
71
143
 
@@ -78,7 +150,7 @@ export default {
78
150
  :mode="mode"
79
151
  :resource="model"
80
152
  :subtypes="[]"
81
- :validation-passed="true"
153
+ :validation-passed="validationPassed"
82
154
  :finish-button-mode="model.enabled ? 'edit' : 'enable'"
83
155
  :can-yaml="false"
84
156
  :errors="errors"
@@ -126,6 +198,7 @@ export default {
126
198
  <div class="col span-6">
127
199
  <LabeledInput
128
200
  v-model:value="username"
201
+ name="username"
129
202
  :label="t(`authConfig.${AUTH_TYPE}.username`)"
130
203
  :mode="mode"
131
204
  required
@@ -134,6 +207,7 @@ export default {
134
207
  <div class="col span-6">
135
208
  <Password
136
209
  v-model:value="password"
210
+ name="password"
137
211
  :label="t(`authConfig.${AUTH_TYPE}.password`)"
138
212
  :mode="mode"
139
213
  required
@@ -1,4 +1,9 @@
1
1
  <script>
2
+ import { ref, computed, provide } from 'vue';
3
+ import { useStore } from 'vuex';
4
+ import { useForm } from 'vee-validate';
5
+ import { toTypedSchema } from '@vee-validate/zod';
6
+ import * as z from 'zod';
2
7
  import Loading from '@shell/components/Loading';
3
8
  import CreateEditView from '@shell/mixins/create-edit-view';
4
9
  import AuthConfig, { SLO_OPTION_VALUES } from '@shell/mixins/auth-config';
@@ -15,7 +20,7 @@ import { RadioGroup } from '@components/Form/Radio';
15
20
  import { Checkbox } from '@components/Form/Checkbox';
16
21
  import { BASE_SCOPES } from '@shell/store/auth';
17
22
  import CopyToClipboardText from '@shell/components/CopyToClipboardText.vue';
18
- import { isValidUrl } from '@shell/utils/validators/setting';
23
+ import { useI18n } from '@shell/composables/useI18n';
19
24
 
20
25
  const PKCE_S256 = 'S256';
21
26
 
@@ -41,7 +46,94 @@ export default {
41
46
  mixins: [CreateEditView, AuthConfig],
42
47
 
43
48
  setup() {
44
- return { PKCE_S256 };
49
+ const store = useStore();
50
+ const { t } = useI18n(store);
51
+
52
+ // These refs sync mixin-state for the composition api
53
+ const modelId = ref(null);
54
+ const sloTypeRef = ref(null);
55
+ const customEndpointEnabled = ref(false);
56
+
57
+ const requiredScopes = computed(() => {
58
+ const scopes = BASE_SCOPES[modelId.value]?.[0];
59
+
60
+ return scopes ? scopes.split(' ') : [];
61
+ });
62
+ const isAmazonCognito = computed(() => modelId.value === 'cognito');
63
+ const isKeycloak = computed(() => modelId.value === 'keycloakoidc');
64
+ const isGenericOidc = computed(() => modelId.value === 'genericoidc');
65
+ const supportsCustomClaims = computed(() => isKeycloak.value || isGenericOidc.value);
66
+ const supportsGroupSearch = computed(() => modelId.value !== 'cognito');
67
+ const requiresCert = computed(() => modelId.value !== 'cognito');
68
+ const requiresAuthEndpoint = computed(() => ['genericoidc', 'keycloakoidc'].includes(modelId.value));
69
+ const sloEndSessionEndpointUiEnabled = computed(() => [SLO_OPTION_VALUES.all, SLO_OPTION_VALUES.both].includes(sloTypeRef.value));
70
+
71
+ // z.preprocess coerces null/undefined to '' before validation. This
72
+ // prevents the raw "Expected string, received null" zod message.
73
+ const coerce = (schema) => z.preprocess((v) => v ?? '', schema);
74
+ const requiredField = (key) => coerce(z.string().min(1, t('validation.required', { key: t(key) })));
75
+ const requiredUrlField = (key) => coerce(z.string().min(1, t('validation.required', { key: t(key) })).url(t('validation.genericUrl')));
76
+ const optionalField = coerce(z.string());
77
+
78
+ // Reactive schema uses computed to reshape when provider or endpoint mode
79
+ // changes.
80
+ const validationSchema = computed(() => toTypedSchema(
81
+ z.object({
82
+ clientId: requiredField('authConfig.oidc.clientId'),
83
+ clientSecret: requiredField('authConfig.oidc.clientSecret'),
84
+
85
+ url: !customEndpointEnabled.value && !isAmazonCognito.value ? requiredUrlField('authConfig.oidc.url') : optionalField,
86
+ realm: !customEndpointEnabled.value && !isAmazonCognito.value ? requiredField('authConfig.oidc.realm') : optionalField,
87
+
88
+ rancherUrl: customEndpointEnabled.value && !isAmazonCognito.value ? requiredField('authConfig.oidc.rancherUrl') : optionalField,
89
+ issuer: customEndpointEnabled.value || isAmazonCognito.value ? requiredField('authConfig.oidc.issuer') : optionalField,
90
+
91
+ authEndpoint: requiresAuthEndpoint.value ? requiredUrlField('authConfig.oidc.authEndpoint') : optionalField,
92
+
93
+ endSessionEndpoint: sloEndSessionEndpointUiEnabled.value ? requiredUrlField('authConfig.oidc.endSessionEndpoint.title') : optionalField,
94
+
95
+ scope: z.preprocess(
96
+ (v) => (Array.isArray(v) ? v : []),
97
+ z.array(z.string()).refine(
98
+ (arr) => requiredScopes.value?.every((s) => arr.includes(s)),
99
+ (arr) => {
100
+ const missing = requiredScopes.value?.filter((s) => !arr.includes(s));
101
+
102
+ return { message: t('authConfig.oidc.scope.missingRequired', { scopes: missing?.join(', '), count: missing?.length }) };
103
+ }
104
+ )
105
+ ),
106
+ })
107
+ ));
108
+
109
+ const showAllErrors = ref(false);
110
+
111
+ provide('vee-show-all-errors', showAllErrors);
112
+
113
+ const { errors, validate } = useForm({ validationSchema });
114
+ const isFormValid = computed(() => Object.keys(errors.value).length === 0);
115
+
116
+ const validateAllFields = async() => {
117
+ await validate();
118
+ showAllErrors.value = true;
119
+ };
120
+
121
+ return {
122
+ PKCE_S256,
123
+ isFormValid,
124
+ validateAllFields,
125
+ modelId,
126
+ sloTypeRef,
127
+ customEndpointEnabled,
128
+ isAmazonCognito,
129
+ isKeycloak,
130
+ isGenericOidc,
131
+ supportsCustomClaims,
132
+ supportsGroupSearch,
133
+ requiresCert,
134
+ requiresAuthEndpoint,
135
+ sloEndSessionEndpointUiEnabled,
136
+ };
45
137
  },
46
138
 
47
139
  data() {
@@ -92,75 +184,11 @@ export default {
92
184
  },
93
185
 
94
186
  validationPassed() {
95
- if ( this.model.enabled && !this.editConfig ) {
187
+ if ( this.model?.enabled && !this.editConfig ) {
96
188
  return true;
97
189
  }
98
190
 
99
- const { clientId, clientSecret } = this.model;
100
- const isMissingAuthEndpoint = (this.requiresAuthEndpoint && !this.model.authEndpoint);
101
- const isMissingScopes = !this.requiredScopes.every((scope) => this.oidcScope.includes(scope));
102
-
103
- if (isMissingAuthEndpoint || isMissingScopes) {
104
- return false;
105
- }
106
-
107
- // make sure that if SLO options are enabled on radio group, field "endSessionEndpoint" is required
108
- if (this.isLogoutAllSupported && this.sloEndSessionEndpointUiEnabled && (!this.model.endSessionEndpoint || !isValidUrl(this.model.endSessionEndpoint))) {
109
- return false;
110
- }
111
-
112
- if (this.isAmazonCognito) {
113
- const { issuer } = this.model;
114
-
115
- return !!(clientId && clientSecret && issuer);
116
- } else if ( !this.customEndpoint.value ) {
117
- const { url, realm } = this.oidcUrls;
118
-
119
- return !!(clientId && clientSecret && url && realm);
120
- } else {
121
- const { rancherUrl, issuer } = this.model;
122
-
123
- return !!(clientId && clientSecret && rancherUrl && issuer);
124
- }
125
- },
126
-
127
- requiresAuthEndpoint() {
128
- return ['genericoidc', 'keycloakoidc'].includes(this.model.id);
129
- },
130
-
131
- /**
132
- * TODO #13457: Refactor scopes to be an array of terms
133
- * Return valid scopes
134
- * The scopes for given auth provider (model.id) have format of ['scope1 scope2 scope3']
135
- */
136
- requiredScopes() {
137
- return this.model.id ? (BASE_SCOPES[this.model.id] || []) ? (BASE_SCOPES[this.model.id] || [])[0].split(' ') : [] : [];
138
- },
139
-
140
- requiresCert() {
141
- // We assume all do, apart from the ones here, which do not
142
- return !(['cognito'].includes(this.model.id));
143
- },
144
-
145
- supportsGroupSearch() {
146
- // We assume all do, apart from the ones here, which do not
147
- return !(['cognito'].includes(this.model.id));
148
- },
149
-
150
- isAmazonCognito() {
151
- return this.model?.id === 'cognito';
152
- },
153
-
154
- isGenericOidc() {
155
- return this.model?.id === 'genericoidc';
156
- },
157
-
158
- isKeycloak() {
159
- return this.model?.id === 'keycloakoidc';
160
- },
161
-
162
- supportsCustomClaims() {
163
- return this.isGenericOidc || this.isKeycloak;
191
+ return this.isFormValid;
164
192
  },
165
193
 
166
194
  isLogoutAllSupported() {
@@ -180,17 +208,24 @@ export default {
180
208
 
181
209
  return sloOptionSelected?.label || '';
182
210
  },
183
-
184
- sloEndSessionEndpointUiEnabled() {
185
- return this.sloType === SLO_OPTION_VALUES.all || this.sloType === SLO_OPTION_VALUES.both;
186
- },
187
211
  },
188
212
 
189
213
  watch: {
190
- fvFormIsValid(newValue) {
214
+ validationPassed(newValue) {
191
215
  this.$emit('validationChanged', !!newValue);
192
216
  },
193
217
 
218
+ 'model.id': {
219
+ handler(newVal) {
220
+ this.modelId = newVal;
221
+ },
222
+ immediate: true,
223
+ },
224
+
225
+ 'customEndpoint.value'(v) {
226
+ this.customEndpointEnabled = v;
227
+ },
228
+
194
229
  'oidcUrls.url'() {
195
230
  this.updateEndpoints();
196
231
  },
@@ -228,6 +263,7 @@ export default {
228
263
 
229
264
  // sloType is defined on shell/mixins/auth-config.js
230
265
  sloType(neu) {
266
+ this.sloTypeRef = neu;
231
267
  switch (neu) {
232
268
  case SLO_OPTION_VALUES.rancher:
233
269
  this.model.logoutAllEnabled = false;
@@ -379,6 +415,7 @@ export default {
379
415
  <div class="col span-6">
380
416
  <LabeledInput
381
417
  v-model:value="model.clientId"
418
+ name="clientId"
382
419
  :label="t(`authConfig.oidc.clientId`)"
383
420
  :mode="mode"
384
421
  required
@@ -388,6 +425,7 @@ export default {
388
425
  <div class="col span-6">
389
426
  <LabeledInput
390
427
  v-model:value="model.clientSecret"
428
+ name="clientSecret"
391
429
  :label="t(`authConfig.oidc.clientSecret`)"
392
430
  :mode="mode"
393
431
  required
@@ -528,6 +566,7 @@ export default {
528
566
  <div class="col span-6">
529
567
  <LabeledInput
530
568
  v-model:value="oidcUrls.url"
569
+ name="url"
531
570
  :label="t(`authConfig.oidc.url`)"
532
571
  :mode="mode"
533
572
  :required="!customEndpoint.value"
@@ -538,6 +577,7 @@ export default {
538
577
  <div class="col span-6">
539
578
  <LabeledInput
540
579
  v-model:value="oidcUrls.realm"
580
+ name="realm"
541
581
  :label="t(`authConfig.oidc.realm`)"
542
582
  :mode="mode"
543
583
  :required="!customEndpoint.value"
@@ -552,6 +592,7 @@ export default {
552
592
  <div class="col span-6">
553
593
  <LabeledInput
554
594
  v-model:value="model.rancherUrl"
595
+ name="rancherUrl"
555
596
  :label="t(`authConfig.oidc.rancherUrl`)"
556
597
  :mode="mode"
557
598
  required
@@ -565,6 +606,7 @@ export default {
565
606
  <div class="col span-6">
566
607
  <LabeledInput
567
608
  v-model:value="model.issuer"
609
+ name="issuer"
568
610
  :label="t(`authConfig.oidc.issuer`)"
569
611
  :mode="mode"
570
612
  required
@@ -575,6 +617,7 @@ export default {
575
617
  <div class="col span-6">
576
618
  <LabeledInput
577
619
  v-model:value="model.authEndpoint"
620
+ name="authEndpoint"
578
621
  :label="t(`authConfig.oidc.authEndpoint`)"
579
622
  :mode="mode"
580
623
  :disabled="!customEndpoint.value"
@@ -635,6 +678,7 @@ export default {
635
678
  <div class="col span-6">
636
679
  <LabeledInput
637
680
  v-model:value="model.issuer"
681
+ name="issuer"
638
682
  :label="t(`authConfig.oidc.issuer`)"
639
683
  :mode="mode"
640
684
  required
@@ -649,6 +693,7 @@ export default {
649
693
  <div class="col span-6">
650
694
  <ArrayList
651
695
  v-model:value="oidcScope"
696
+ name="scope"
652
697
  :mode="mode"
653
698
  :title="t('authConfig.oidc.scope.label')"
654
699
  :value-placeholder="t('authConfig.oidc.scope.placeholder')"
@@ -686,6 +731,7 @@ export default {
686
731
  <div class="col span-6">
687
732
  <LabeledInput
688
733
  v-model:value="model.endSessionEndpoint"
734
+ name="endSessionEndpoint"
689
735
  :tooltip="t('authConfig.oidc.endSessionEndpoint.tooltip')"
690
736
  :label="t('authConfig.oidc.endSessionEndpoint.title')"
691
737
  :mode="mode"