@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.4
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.
- package/assets/styles/global/_layout.scss +4 -0
- package/assets/translations/en-us.yaml +144 -41
- package/assets/translations/zh-hans.yaml +1 -7
- package/chart/monitoring/ClusterSelector.vue +0 -21
- package/chart/monitoring/prometheus/index.vue +6 -3
- package/components/CruResource.vue +161 -14
- package/components/ExplorerMembers.vue +8 -4
- package/components/ExplorerProjectsNamespaces.vue +10 -6
- package/components/GrowlManager.vue +4 -0
- package/components/MgmtNodeList.vue +184 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
- package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
- package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
- package/components/ResourceDetail/index.vue +1 -1
- package/components/ResourceList/Masthead.vue +7 -1
- package/components/ResourceList/index.vue +82 -1
- package/components/RichTranslation.vue +5 -2
- package/components/Setting.vue +1 -0
- package/components/SubtleLink.vue +31 -6
- package/components/Tabbed/Tab.vue +29 -3
- package/components/Tabbed/index.vue +25 -3
- package/components/TableOfContents/TableOfContents.vue +109 -0
- package/components/TableOfContents/composables.ts +258 -0
- package/components/Window/ContainerShell.vue +21 -11
- package/components/Window/__tests__/ContainerShell.test.ts +107 -37
- package/components/Wizard.vue +9 -4
- package/components/fleet/AppCoChartGrid.vue +401 -0
- package/components/fleet/AppCoEmptyState.vue +127 -0
- package/components/fleet/AppCoPageHeader.vue +119 -0
- package/components/fleet/AppCoVersionSelect.vue +70 -0
- package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
- package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
- package/components/fleet/FleetClusterTargets/index.vue +189 -146
- package/components/fleet/FleetIntro.vue +7 -3
- package/components/fleet/FleetNoWorkspaces.vue +7 -3
- package/components/fleet/FleetSecretSelector.vue +5 -3
- package/components/fleet/FleetValuesFrom.vue +8 -2
- package/components/fleet/GitRepoTargetTab.vue +0 -2
- package/components/fleet/HelmOpAdvancedTab.vue +19 -53
- package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
- package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
- package/components/fleet/HelmOpResourcesSection.vue +82 -0
- package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
- package/components/fleet/HelmOpTargetTab.vue +64 -60
- package/components/fleet/HelmOpValuesTab.vue +129 -105
- package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
- package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
- package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
- package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
- package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
- package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
- package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
- package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
- package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
- package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
- package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
- package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
- package/components/fleet/dashboard/Empty.vue +8 -4
- package/components/fleet/dashboard/ResourceCard.vue +28 -0
- package/components/fleet/dashboard/ResourceDetails.vue +28 -0
- package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
- package/components/form/ArrayList.vue +61 -4
- package/components/form/KeyValue.vue +23 -2
- package/components/form/LabeledSelect.vue +39 -1
- package/components/form/Labels.vue +22 -3
- package/components/form/NameNsDescription.vue +13 -5
- package/components/form/ResourceTabs/index.vue +1 -0
- package/components/form/__tests__/NameNsDescription.test.ts +75 -0
- package/components/formatter/InternalExternalIP.vue +10 -4
- package/components/formatter/ServiceTargets.vue +26 -7
- package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
- package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
- package/components/nav/Header.vue +4 -0
- package/components/nav/TopLevelMenu.vue +7 -2
- package/components/nav/__tests__/Header.test.ts +15 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
- package/components/templates/default.vue +9 -4
- package/components/templates/home.vue +9 -4
- package/components/templates/plain.vue +9 -4
- package/composables/useHelmOpResources.test.ts +56 -0
- package/composables/useHelmOpResources.ts +32 -0
- package/composables/useStateColor.test.ts +325 -0
- package/composables/useStateColor.ts +128 -0
- package/config/home-links.js +1 -1
- package/config/labels-annotations.js +1 -0
- package/config/product/explorer.js +17 -4
- package/config/product/manager.js +2 -0
- package/config/router/index.js +16 -0
- package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
- package/config/router/navigation-guards/authentication.js +10 -4
- package/config/router/routes.js +20 -6
- package/config/settings.ts +0 -2
- package/config/table-headers.js +3 -4
- package/config/types.js +9 -0
- package/core/plugin-products-base.ts +3 -3
- package/core/plugin-types.ts +83 -30
- package/core/plugin.ts +3 -0
- package/core/types-provisioning.ts +34 -1
- package/core/types.ts +15 -2
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
- package/detail/__tests__/workload.test.ts +3 -152
- package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +30 -4
- package/detail/workload/index.vue +12 -55
- package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
- package/edit/auth/__tests__/azuread.test.ts +34 -9
- package/edit/auth/__tests__/github.test.ts +234 -0
- package/edit/auth/__tests__/oidc.test.ts +26 -6
- package/edit/auth/__tests__/saml.test.ts +196 -0
- package/edit/auth/azuread.vue +128 -95
- package/edit/auth/github.vue +72 -13
- package/edit/auth/ldap/__tests__/index.test.ts +206 -0
- package/edit/auth/ldap/config.vue +8 -0
- package/edit/auth/ldap/index.vue +75 -1
- package/edit/auth/oidc.vue +119 -73
- package/edit/auth/saml.vue +76 -12
- package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
- package/edit/fleet.cattle.io.helmop.vue +491 -136
- package/edit/management.cattle.io.user.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/list/group.principal.vue +5 -4
- package/list/harvesterhci.io.management.cluster.vue +8 -9
- package/list/management.cattle.io.user.vue +12 -9
- package/list/provisioning.cattle.io.cluster.vue +16 -10
- package/mixins/__tests__/auth-config.test.ts +90 -0
- package/mixins/__tests__/chart.test.ts +94 -0
- package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
- package/mixins/auth-config.js +7 -0
- package/mixins/chart.js +11 -2
- package/mixins/child-hook.js +12 -6
- package/mixins/create-edit-view/impl.js +5 -3
- package/mixins/resource-fetch-api-pagination.js +21 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
- package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
- package/models/__tests__/fleet-application.test.ts +175 -0
- package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
- package/models/__tests__/management.cattle.io.node.ts +22 -0
- package/models/__tests__/namespace.test.ts +36 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
- package/models/__tests__/workload.test.ts +401 -26
- package/models/catalog.cattle.io.clusterrepo.js +28 -4
- package/models/compliance.cattle.io.clusterscan.js +39 -4
- package/models/fleet-application.js +4 -0
- package/models/fleet.cattle.io.helmop.js +20 -1
- package/models/management.cattle.io.cluster.js +18 -2
- package/models/management.cattle.io.node.js +44 -3
- package/models/namespace.js +1 -1
- package/models/pod.js +33 -1
- package/models/provisioning.cattle.io.cluster.js +5 -5
- package/models/workload.js +108 -13
- package/models/workload.service.js +5 -0
- package/package.json +14 -13
- package/pages/about.vue +5 -6
- package/pages/auth/login.vue +0 -35
- package/pages/auth/setup.vue +11 -0
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
- package/pages/c/_cluster/apps/charts/chart.vue +2 -1
- package/pages/c/_cluster/apps/charts/index.vue +48 -10
- package/pages/c/_cluster/apps/charts/install.vue +122 -116
- package/pages/c/_cluster/auth/roles/index.vue +5 -4
- package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
- package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
- package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
- package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
- package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
- package/pages/c/_cluster/fleet/application/create.vue +187 -136
- package/pages/c/_cluster/fleet/application/index.vue +5 -3
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
- package/pages/c/_cluster/fleet/index.vue +2 -2
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
- package/pages/c/_cluster/uiplugins/index.vue +15 -0
- package/pages/fail-whale.vue +16 -11
- package/pages/home.vue +16 -46
- package/plugins/clean-html.d.ts +9 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
- package/plugins/dashboard-store/resource-class.js +62 -7
- package/plugins/steve/__tests__/actions.test.ts +212 -0
- package/plugins/steve/actions.js +96 -0
- package/plugins/steve/steve-pagination-utils.ts +1 -1
- package/rancher-components/Accordion/Accordion.vue +53 -9
- package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
- package/rancher-components/Form/Radio/RadioButton.vue +17 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
- package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
- package/rancher-components/RcButton/RcButton.test.ts +103 -0
- package/rancher-components/RcButton/RcButton.vue +94 -15
- package/rancher-components/RcButton/types.ts +3 -0
- package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
- package/rancher-components/RcSection/RcSection.vue +28 -3
- package/scripts/extension/helm/package/Dockerfile +1 -1
- package/scripts/test-plugins-build.sh +2 -1
- package/store/__tests__/notifications.test.ts +434 -0
- package/store/catalog.js +57 -0
- package/store/plugins.js +7 -4
- package/types/components/buttonGroup.ts +5 -0
- package/types/shell/index.d.ts +104 -70
- package/utils/__tests__/auth.test.ts +273 -0
- package/utils/__tests__/computed.test.ts +193 -0
- package/utils/__tests__/cspAdaptor.test.ts +163 -0
- package/utils/__tests__/dom.test.ts +81 -0
- package/utils/__tests__/duration.test.ts +37 -1
- package/utils/__tests__/dynamic-importer.test.ts +102 -0
- package/utils/__tests__/fleet-appco.test.ts +312 -0
- package/utils/__tests__/monitoring.test.ts +130 -0
- package/utils/__tests__/object.test.ts +22 -0
- package/utils/__tests__/platform.test.ts +91 -0
- package/utils/__tests__/position.test.ts +237 -0
- package/utils/__tests__/provider.test.ts +51 -1
- package/utils/__tests__/queue.test.ts +232 -0
- package/utils/__tests__/release-notes.test.ts +221 -0
- package/utils/__tests__/router.test.js +254 -1
- package/utils/__tests__/select.test.ts +208 -0
- package/utils/__tests__/time.test.ts +265 -1
- package/utils/__tests__/title.test.ts +47 -0
- package/utils/__tests__/width.test.ts +53 -0
- package/utils/__tests__/window.test.ts +158 -0
- package/utils/__tests__/xccdf.test.ts +126 -6
- package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
- package/utils/crypto/__tests__/index.test.ts +144 -0
- package/utils/duration.ts +104 -0
- package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
- package/utils/dynamic-content/info.ts +2 -1
- package/utils/error.js +13 -0
- package/utils/fleet-appco.ts +323 -0
- package/utils/object.js +22 -2
- package/utils/provider.ts +12 -0
- package/utils/validators/__tests__/container-images.test.ts +104 -0
- package/utils/validators/__tests__/flow-output.test.ts +91 -0
- package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
- package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
- package/utils/xccdf.ts +39 -42
- package/vue.config.js +1 -1
- package/pages/support/index.vue +0 -264
- 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')"
|
package/edit/auth/ldap/index.vue
CHANGED
|
@@ -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="
|
|
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
|
package/edit/auth/oidc.vue
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
187
|
+
if ( this.model?.enabled && !this.editConfig ) {
|
|
96
188
|
return true;
|
|
97
189
|
}
|
|
98
190
|
|
|
99
|
-
|
|
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
|
-
|
|
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"
|