@rancher/shell 0.3.5 → 0.3.7

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 (120) hide show
  1. package/assets/images/providers/outscale.svg +19 -0
  2. package/assets/styles/base/_basic.scss +18 -0
  3. package/assets/styles/base/_mixins.scss +0 -11
  4. package/assets/styles/base/_variables.scss +2 -4
  5. package/assets/styles/global/_button.scss +12 -2
  6. package/assets/translations/en-us.yaml +35 -1
  7. package/assets/translations/zh-hans.yaml +30 -10
  8. package/chart/gatekeeper.vue +3 -2
  9. package/chart/istio.vue +29 -3
  10. package/components/BrandImage.vue +1 -4
  11. package/components/Carousel.vue +85 -37
  12. package/components/EtcdInfoBanner.vue +7 -3
  13. package/components/ExplorerMembers.vue +100 -5
  14. package/components/ExplorerProjectsNamespaces.vue +32 -2
  15. package/components/GrafanaDashboard.vue +9 -2
  16. package/components/SortableTable/index.vue +23 -11
  17. package/components/SortableTable/selection.js +58 -50
  18. package/components/Wizard.vue +4 -2
  19. package/components/auth/AuthBanner.vue +6 -0
  20. package/components/auth/RoleDetailEdit.vue +2 -2
  21. package/components/form/HookOption.vue +14 -10
  22. package/components/form/Labels.vue +32 -27
  23. package/components/form/MatchExpressions.vue +2 -2
  24. package/components/form/Members/ClusterPermissionsEditor.vue +32 -7
  25. package/components/form/NameNsDescription.vue +1 -1
  26. package/components/form/ProjectMemberEditor.vue +46 -21
  27. package/components/form/Tolerations.vue +4 -1
  28. package/components/form/ValueFromResource.vue +14 -9
  29. package/components/form/WorkloadPorts.vue +2 -2
  30. package/components/form/__tests__/NameNsDescription.ts +27 -0
  31. package/components/formatter/WorkloadHealthScale.vue +8 -2
  32. package/components/nav/NamespaceFilter.vue +8 -0
  33. package/{nuxt/components → components/nuxt}/nuxt.js +1 -1
  34. package/{nuxt → config}/middleware.js +8 -8
  35. package/config/product/explorer.js +24 -3
  36. package/config/query-params.js +1 -0
  37. package/config/router.js +1 -1
  38. package/{nuxt → config}/store.js +82 -79
  39. package/config/table-headers.js +46 -12
  40. package/config/types.js +7 -0
  41. package/core/plugin.ts +4 -2
  42. package/core/types.ts +258 -1
  43. package/creators/app/files/tsconfig.json +0 -1
  44. package/creators/app/files/vue.config.js +0 -1
  45. package/creators/pkg/files/.github/workflows/build-extension.yml +3 -4
  46. package/creators/pkg/files/tsconfig.json +0 -1
  47. package/creators/pkg/pkg.package.json +3 -3
  48. package/detail/constraints.gatekeeper.sh.constraint.vue +14 -7
  49. package/detail/fleet.cattle.io.clustergroup.vue +7 -1
  50. package/edit/auth/ldap/config.vue +21 -1
  51. package/edit/auth/saml.vue +132 -37
  52. package/edit/fleet.cattle.io.gitrepo.vue +16 -1
  53. package/edit/logging.banzaicloud.io.output/index.vue +18 -5
  54. package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
  55. package/edit/namespace.vue +12 -8
  56. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -4
  57. package/edit/provisioning.cattle.io.cluster/import.vue +23 -25
  58. package/edit/provisioning.cattle.io.cluster/rke2.vue +96 -18
  59. package/edit/workload/mixins/workload.js +6 -7
  60. package/edit/workload/storage/Mount.vue +3 -3
  61. package/initialize/App.js +206 -0
  62. package/{nuxt → initialize}/client.js +406 -360
  63. package/{nuxt → initialize}/index.js +21 -22
  64. package/layouts/standalone.vue +13 -0
  65. package/list/catalog.cattle.io.clusterrepo.vue +1 -0
  66. package/list/rbac.authorization.k8s.io.clusterrolebinding.vue +48 -0
  67. package/list/workload.vue +6 -4
  68. package/mixins/chart.js +29 -1
  69. package/mixins/fetch.client.js +95 -0
  70. package/{nuxt/mixins → mixins}/fetch.server.js +30 -26
  71. package/mixins/labeled-form-element.ts +2 -2
  72. package/models/constraints.gatekeeper.sh.constraint.js +37 -0
  73. package/models/pod.js +4 -0
  74. package/models/rbac.authorization.k8s.io.clusterrolebinding.js +16 -0
  75. package/models/rbac.authorization.k8s.io.rolebinding.js +16 -0
  76. package/package.json +9 -13
  77. package/pages/c/_cluster/apps/charts/install.vue +61 -39
  78. package/pages/diagnostic.vue +32 -25
  79. package/pages/rio/mesh.vue +1 -2
  80. package/pkg/tsconfig.json +0 -1
  81. package/plugins/clean-html-directive.js +3 -0
  82. package/plugins/dashboard-store/index.js +1 -1
  83. package/plugins/plugin.js +0 -14
  84. package/plugins/portal-vue.js +4 -0
  85. package/rancher-components/components/Banner/Banner.test.ts +3 -5
  86. package/rancher-components/components/Banner/Banner.vue +1 -0
  87. package/rancher-components/components/Form/Radio/RadioButton.test.ts +31 -0
  88. package/rancher-components/components/Form/Radio/RadioButton.vue +14 -3
  89. package/scripts/extension/publish +42 -23
  90. package/scripts/serve-pkgs +6 -2
  91. package/store/type-map.js +1 -1
  92. package/tsconfig.json +0 -1
  93. package/types/rancher/index.d.ts +2 -0
  94. package/types/shell/index.d.ts +353 -284
  95. package/utils/__tests__/grafana.test.ts +44 -0
  96. package/utils/axios.js +190 -0
  97. package/{nuxt → utils}/cookie-universal-nuxt.js +7 -6
  98. package/utils/dom.js +15 -0
  99. package/utils/gc/gc.ts +1 -1
  100. package/utils/grafana.js +35 -16
  101. package/{nuxt/utils.js → utils/nuxt.js} +265 -236
  102. package/utils/router.scrollBehavior.js +1 -1
  103. package/vue.config.js +30 -19
  104. package/nuxt/App.js +0 -210
  105. package/nuxt/axios.js +0 -186
  106. package/nuxt/empty.js +0 -1
  107. package/nuxt/jsonp.js +0 -82
  108. package/nuxt/loading.html +0 -39
  109. package/nuxt/mixins/fetch.client.js +0 -90
  110. package/nuxt/portal-vue.js +0 -4
  111. package/nuxt/server.js +0 -312
  112. package/nuxt/views/app.template.html +0 -9
  113. package/nuxt/views/error.html +0 -23
  114. package/plugins/dashboard-store/extensions.js +0 -22
  115. /package/{nuxt/components → components/nuxt}/nuxt-build-indicator.vue +0 -0
  116. /package/{nuxt/components → components/nuxt}/nuxt-child.js +0 -0
  117. /package/{nuxt/components → components/nuxt}/nuxt-error.vue +0 -0
  118. /package/{nuxt/components → components/nuxt}/nuxt-link.client.js +0 -0
  119. /package/{nuxt/components → components/nuxt}/nuxt-link.server.js +0 -0
  120. /package/{nuxt/components → components/nuxt}/nuxt-loading.vue +0 -0
package/core/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ProductFunction } from './plugin';
1
2
  import { RouteConfig } from 'vue-router';
2
3
 
3
4
  // package.json metadata
@@ -136,6 +137,255 @@ export type LocationConfig = {
136
137
  mode?: string[]
137
138
  };
138
139
 
140
+ export interface ProductOptions {
141
+ /**
142
+ * The category this product belongs under. i.e. 'config'
143
+ */
144
+ category?: string;
145
+
146
+ /**
147
+ * Hide the Copy KubeConfig button in the header
148
+ */
149
+ hideCopyConfig?: boolean;
150
+
151
+ /**
152
+ * Hide the Download KubeConfig button in the header
153
+ */
154
+ hideKubeConfig?: boolean;
155
+
156
+ /**
157
+ * Hide the Kubectl Shell button in the header
158
+ */
159
+ hideKubeShell?: boolean;
160
+
161
+ /**
162
+ * Hide the Namespace location
163
+ */
164
+ hideNamespaceLocation?: boolean;
165
+
166
+ /**
167
+ * Hide the system resources
168
+ */
169
+
170
+ hideSystemResources?: boolean;
171
+ /**
172
+ * The icon that should be displayed beside this item in the navigation.
173
+ */
174
+ icon?: string,
175
+
176
+ /**
177
+ * Only load the product if the feature is present
178
+ */
179
+ ifFeature?: string | RegExp;
180
+
181
+ /**
182
+ * Only load the product if the type is present
183
+ */
184
+ ifHave?: string;
185
+
186
+ /**
187
+ * Only load the product if the group is present
188
+ */
189
+ ifHaveGroup?: string | RegExp;
190
+
191
+ /**
192
+ * Only load the product if the type is present
193
+ */
194
+ ifHaveType?: string | RegExp;
195
+
196
+ /**
197
+ * The vuex store that this product should use by default i.e. 'management'
198
+ */
199
+ inStore?: string;
200
+
201
+ /**
202
+ * Show the cluster switcher in the navigation
203
+ */
204
+ showClusterSwitcher?: boolean;
205
+
206
+ /**
207
+ * Show the namespace filter in the header
208
+ */
209
+ showNamespaceFilter?: boolean;
210
+
211
+ /**
212
+ * A number used to determine where in navigation this item will be placed. The highest number will be at the top of the list.
213
+ */
214
+ weight?: number;
215
+
216
+ /**
217
+ * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
218
+ */
219
+ // ifHaveVerb: string | RegExp;
220
+ // removable: string;
221
+ // showWorkspaceSwitcher: boolean;
222
+ // supportRoute: string;
223
+ // to: string;
224
+ // typeStoreMap: string;
225
+ }
226
+
227
+ export interface HeaderOptions {
228
+ /**
229
+ * Name of the header. This should be unique.
230
+ */
231
+ name?: string;
232
+
233
+ /**
234
+ * A translation key where the resulting string will show in the table column
235
+ */
236
+ labelKey?: string;
237
+
238
+ /**
239
+ * A string which represents the path to access the value from the row object i.e. `row.meta.value`.
240
+ */
241
+ value?: string;
242
+
243
+ /**
244
+ * A string which represents the path to access the value from the row object which we'll use to sort i.e. `row.meta.value`
245
+ */
246
+ sort?: string;
247
+
248
+ /**
249
+ * A string which represents the path to access the value from the row object which we'll use to search i.e. `row.meta.value`
250
+ */
251
+ search?: string;
252
+
253
+ /**
254
+ * Number of pixels the column should be in the table
255
+ */
256
+ width?: number;
257
+
258
+ /**
259
+ * The name of a custom formatter. The available formatters can bee seen in `@rancher/shell/components/formatter`
260
+ */
261
+ formatter?: string;
262
+
263
+ /**
264
+ * These options are dependent on the formatter that's chosen. Examples can be seen in `@rancher/shell/components/formatter` and `@rancher/shell/config/table-headers`
265
+ */
266
+ formatterOpts?: any;
267
+
268
+ /**
269
+ * Provide a function which accets a row and returns the value that should be displayed in the column
270
+ * @param row This can be any value which represents the row
271
+ * @returns Can return {@link string | number | null | undefined} to display in the column
272
+ */
273
+ getValue?: (row: any) => string | number | null | undefined;
274
+ }
275
+
276
+ export interface ConfigureTypeOptions {
277
+ /**
278
+ * The resource can edit/show yaml
279
+ */
280
+ canYaml?: boolean;
281
+
282
+ /**
283
+ * Modify the way the name looks when displayed
284
+ */
285
+ displayName?: string;
286
+
287
+ /**
288
+ * New resources can be created of this type
289
+ */
290
+ isCreatable?: boolean;
291
+
292
+ /**
293
+ * Resources of this type can be deleted/removed
294
+ */
295
+ isRemovable?: boolean;
296
+
297
+ /**
298
+ * This type should be grouped by namespaces when displayed in a table
299
+ */
300
+ namespaced?: boolean;
301
+
302
+ /**
303
+ * Show the age column in when displaying this type in a table
304
+ */
305
+ showAge?: boolean;
306
+
307
+ /**
308
+ * Show the masthead at the top of the list view of this type
309
+ */
310
+ showListMasthead?: boolean;
311
+
312
+ /**
313
+ * Show the state column in when displaying this type in a table
314
+ */
315
+ showState?: boolean;
316
+
317
+ /**
318
+ * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
319
+ */
320
+ // alias
321
+ // customRoute
322
+ // customRoute
323
+ // depaginate
324
+ // graphConfig
325
+ // hasGraph
326
+ // isEditable
327
+ // limit
328
+ // listGroups
329
+ // localOnly
330
+ // location
331
+ // match
332
+ // realResource
333
+ // resource
334
+ // resourceDetail
335
+ // resourceEdit
336
+ // showConfigView
337
+ }
338
+
339
+ export interface DSLReturnType {
340
+ /**
341
+ * Register multiple types by name and place them all in a group if desired. Primarily used for grouping things in the cluster explorer navigation.
342
+ * @param types A list of types that are going to be registered
343
+ * @param group Conditionally a group you want to places all the types in
344
+ * @returns {@link void}
345
+ */
346
+ basicType: (types: string[], group?: string) => void;
347
+
348
+ /**
349
+ * Configure a myriad of options for the specified type
350
+ * @param type The type to be configured
351
+ * @param options {@link ConfigureTypeOptions}
352
+ * @returns {@link void}
353
+ */
354
+ configureType: (type: string, options: ConfigureTypeOptions) => void;
355
+
356
+ /**
357
+ * Register the headers/columns that should be used when rendering a table for the specified type.
358
+ * @param type The type you'd like to register headers/columns for.
359
+ * @param headers {@link HeaderOptions[]}
360
+ * @returns {@link void}
361
+ */
362
+ headers: (type: string, headers: HeaderOptions[]) => void;
363
+
364
+ /**
365
+ * Create and register a new product
366
+ * @param options {@link ProductOptions}
367
+ * @returns {@link void}
368
+ */
369
+ product: (options: ProductOptions) => void;
370
+
371
+ /**
372
+ * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
373
+ */
374
+ // componentForType: (type: string, replacementType: string)
375
+ // groupBy: (type: string, field: string)
376
+ // hideBulkActions: (type: string, field)
377
+ // ignoreGroup: (regexOrString)
378
+ // ignoreType: (regexOrString)
379
+ // mapGroup: (match, replace)
380
+ // mapType: (match, replace)
381
+ // moveType: (match, group)
382
+ // setGroupDefaultType: (input, defaultType)
383
+ // spoofedType: (obj)
384
+ // virtualType: (obj)
385
+ // weightGroup: (input, weight, forBasic)
386
+ // weightType: (input, weight, forBasic)
387
+ }
388
+
139
389
  /**
140
390
  * Interface for a Dashboard plugin
141
391
  */
@@ -144,7 +394,7 @@ export interface IPlugin {
144
394
  * Add a product
145
395
  * @param importFn Function that will import the module containing a product definition
146
396
  */
147
- addProduct(importFn: Function): void;
397
+ addProduct(importFn: ProductFunction): void;
148
398
 
149
399
  /**
150
400
  * Add a locale to the i18n store
@@ -249,4 +499,11 @@ export interface IPlugin {
249
499
  * @param {Function} fn function that dynamically loads the module for the thing being registered
250
500
  */
251
501
  register(type: string, name: string, fn: Function): void;
502
+
503
+ /**
504
+ * Will return all of the configuration functions used for creating a new product.
505
+ * @param store The store that was passed to the function that's passed to `plugin.addProduct(function)`
506
+ * @param productName The name of the new product. This name is displayed in the navigation.
507
+ */
508
+ DSL(store: any, productName: string): DSLReturnType;
252
509
  }
@@ -31,7 +31,6 @@
31
31
  ],
32
32
  "types": [
33
33
  "@types/node",
34
- "@nuxt/types",
35
34
  "cypress",
36
35
  "rancher",
37
36
  "shell"
@@ -3,5 +3,4 @@ const config = require('@rancher/shell/vue.config');
3
3
  module.exports = config(__dirname, {
4
4
  excludes: [],
5
5
  // excludes: ['fleet', 'example']
6
- // autoLoad: ['fleet', 'example']
7
6
  });
@@ -64,7 +64,7 @@ jobs:
64
64
  shell: bash
65
65
  id: build_script
66
66
  run: |
67
- yarn publish-pkgs -s "${{ github.repository }}" -b main
67
+ yarn publish-pkgs -s "${{ github.repository }}" -b gh-pages
68
68
 
69
69
  - name: Upload charts artifact
70
70
  if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
@@ -83,8 +83,7 @@ jobs:
83
83
  - name: Checkout
84
84
  uses: actions/checkout@v3
85
85
  with:
86
- fetch-depth: 0
87
- ref: main
86
+ ref: gh-pages
88
87
 
89
88
  - name: Configure Git
90
89
  run: |
@@ -105,7 +104,7 @@ jobs:
105
104
  - name: Run chart-releaser
106
105
  uses: helm/chart-releaser-action@v1.4.1
107
106
  with:
108
- charts_dir: ./charts
107
+ charts_dir: ./charts/*
109
108
  env:
110
109
  CR_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
111
110
  CR_SKIP_EXISTING: true
@@ -23,7 +23,6 @@
23
23
  "@types/node",
24
24
  "@types/jest",
25
25
  "@types/lodash",
26
- "@nuxt/types",
27
26
  "rancher",
28
27
  "shell"
29
28
  ],
@@ -9,9 +9,9 @@
9
9
  "node": ">=12"
10
10
  },
11
11
  "devDependencies": {
12
- "@vue/cli-plugin-babel": "~4.5.0",
13
- "@vue/cli-service": "~4.5.0",
14
- "@vue/cli-plugin-typescript": "^4.5.15"
12
+ "@vue/cli-plugin-babel": "4.5.18",
13
+ "@vue/cli-service": "4.5.18",
14
+ "@vue/cli-plugin-typescript": "4.5.18"
15
15
  },
16
16
  "browserslist": [
17
17
  "> 1%",
@@ -2,18 +2,26 @@
2
2
  import CreateEditView from '@shell/mixins/create-edit-view';
3
3
  import SortableTable from '@shell/components/SortableTable';
4
4
  import Banner from '@components/Banner/Banner.vue';
5
- import { CONSTRAINT_VIOLATION_RESOURCE_LINK, CONSTRAINT_VIOLATION_MESSAGE, CONSTRAINT_VIOLATION_TYPE } from '@shell/config/table-headers';
5
+ import {
6
+ CONSTRAINT_VIOLATION_RESOURCE_LINK,
7
+ CONSTRAINT_VIOLATION_MESSAGE,
8
+ CONSTRAINT_VIOLATION_TYPE,
9
+ CONSTRAINT_VIOLATION_NAMESPACE,
10
+ } from '@shell/config/table-headers';
6
11
 
7
12
  export default {
8
13
  components: { Banner, SortableTable },
9
14
  mixins: [CreateEditView],
10
15
  data() {
16
+ const headers = [
17
+ CONSTRAINT_VIOLATION_TYPE,
18
+ CONSTRAINT_VIOLATION_NAMESPACE,
19
+ CONSTRAINT_VIOLATION_RESOURCE_LINK,
20
+ CONSTRAINT_VIOLATION_MESSAGE
21
+ ];
22
+
11
23
  return {
12
- headers: [
13
- CONSTRAINT_VIOLATION_TYPE,
14
- CONSTRAINT_VIOLATION_RESOURCE_LINK,
15
- CONSTRAINT_VIOLATION_MESSAGE
16
- ],
24
+ headers,
17
25
  violations: this.value.violations.map((violation, i) => ({ ...violation, id: i }))
18
26
  };
19
27
  }
@@ -46,7 +54,6 @@ export default {
46
54
  <SortableTable
47
55
  :headers="headers"
48
56
  :rows="violations"
49
- :search="false"
50
57
  :table-actions="false"
51
58
  :row-actions="false"
52
59
  :paging="true"
@@ -5,6 +5,7 @@ import FleetClusters from '@shell/components/fleet/FleetClusters';
5
5
  import ResourceTabs from '@shell/components/form/ResourceTabs';
6
6
  import Tab from '@shell/components/Tabbed/Tab';
7
7
  import { FLEET } from '@shell/config/types';
8
+ import { allHash } from '@shell/utils/promise';
8
9
 
9
10
  export default {
10
11
  name: 'DetailClusterGroup',
@@ -25,7 +26,12 @@ export default {
25
26
  },
26
27
 
27
28
  async fetch() {
28
- await this.$store.dispatch('management/findAll', { type: FLEET.CLUSTER });
29
+ const hash = {
30
+ workspaces: this.$store.dispatch('cluster/findAll', { type: FLEET.WORKSPACE }),
31
+ FleetClusters: this.$store.dispatch('management/findAll', { type: FLEET.CLUSTER }),
32
+ };
33
+
34
+ await allHash(hash);
29
35
  },
30
36
 
31
37
  computed: {
@@ -5,6 +5,7 @@ import { Checkbox } from '@components/Form/Checkbox';
5
5
  import UnitInput from '@shell/components/form/UnitInput';
6
6
  import { Banner } from '@components/Banner';
7
7
  import FileSelector from '@shell/components/form/FileSelector';
8
+ import { OKTA, SHIBBOLETH } from '../saml';
8
9
 
9
10
  const DEFAULT_NON_TLS_PORT = 389;
10
11
  const DEFAULT_TLS_PORT = 636;
@@ -33,6 +34,11 @@ export default {
33
34
  type: {
34
35
  type: String,
35
36
  required: true
37
+ },
38
+
39
+ isCreate: {
40
+ type: Boolean,
41
+ default: false
36
42
  }
37
43
 
38
44
  },
@@ -46,9 +52,17 @@ export default {
46
52
  model: this.value,
47
53
  hostname: this.value.servers.join(','),
48
54
  serverSetting: null,
55
+ OKTA
49
56
  };
50
57
  },
51
58
 
59
+ computed: {
60
+ // Does the auth provider support LDAP for search in addition to SAML?
61
+ isSamlProvider() {
62
+ return this.type === SHIBBOLETH || this.type === OKTA;
63
+ }
64
+ },
65
+
52
66
  watch: {
53
67
  hostname(neu, old) {
54
68
  this.value.servers = neu.split(',');
@@ -232,6 +246,12 @@ export default {
232
246
  <div class="row">
233
247
  <h3> {{ t('authConfig.ldap.customizeSchema') }}</h3>
234
248
  </div>
249
+ <Banner
250
+ v-if="type === OKTA && isCreate"
251
+ class="row"
252
+ color="info"
253
+ label-key="authConfig.ldap.oktaSchema"
254
+ />
235
255
  <div class="row">
236
256
  <div class="col span-6">
237
257
  <h4>{{ t('authConfig.ldap.users') }}</h4>
@@ -361,7 +381,7 @@ export default {
361
381
  />
362
382
  </div>
363
383
  <div
364
- v-if="type!=='shibboleth'"
384
+ v-if="!isSamlProvider"
365
385
  class=" col span-6"
366
386
  >
367
387
  <RadioGroup
@@ -11,6 +11,31 @@ import FileSelector from '@shell/components/form/FileSelector';
11
11
  import AuthBanner from '@shell/components/auth/AuthBanner';
12
12
  import config from '@shell/edit/auth/ldap/config';
13
13
 
14
+ export const SHIBBOLETH = 'shibboleth';
15
+ export const OKTA = 'okta';
16
+
17
+ // Standard LDAP defaults
18
+ const LDAP_DEFAULTS = {
19
+ connectionTimeout: 5000,
20
+ groupDNAttribute: 'entryDN',
21
+ groupMemberMappingAttribute: 'member',
22
+ groupMemberUserAttribute: 'entryDN',
23
+ groupNameAttribute: 'cn',
24
+ groupObjectClass: 'groupOfNames',
25
+ groupSearchAttribute: 'cn',
26
+ nestedGroupMembershipEnabled: false,
27
+ port: 389,
28
+ servers: [],
29
+ starttls: false,
30
+ tls: false,
31
+ disabledStatusBitmask: 0,
32
+ userLoginAttribute: 'uid',
33
+ userMemberAttribute: 'memberOf',
34
+ userNameAttribute: 'cn',
35
+ userObjectClass: 'inetOrgPerson',
36
+ userSearchAttribute: 'uid|sn|givenName'
37
+ };
38
+
14
39
  export default {
15
40
  components: {
16
41
  Loading,
@@ -26,7 +51,10 @@ export default {
26
51
 
27
52
  mixins: [CreateEditView, AuthConfig],
28
53
  data() {
29
- return { showLdap: false };
54
+ return {
55
+ showLdap: false,
56
+ showLdapDetails: false,
57
+ };
30
58
  },
31
59
 
32
60
  computed: {
@@ -42,32 +70,32 @@ export default {
42
70
  return { enabled: true, ...this.model };
43
71
  },
44
72
 
73
+ // Does the auth provider support LDAP for search in addition to SAML?
74
+ supportsLDAPSearch() {
75
+ return this.NAME === SHIBBOLETH || this.NAME === OKTA;
76
+ },
77
+
78
+ ldapHosts() {
79
+ const hosts = this.model?.openLdapConfig.servers || [];
80
+
81
+ return hosts.join(',');
82
+ },
83
+
84
+ ldapProtocol() {
85
+ if (this.model?.openLdapConfig?.starttls) {
86
+ return this.t('authConfig.ldap.protocols.starttls');
87
+ } else if (this.model?.openLdapConfig?.tls) {
88
+ return this.t('authConfig.ldap.protocols.tls');
89
+ }
90
+
91
+ return this.t('authConfig.ldap.protocols.ldap');
92
+ }
45
93
  },
46
94
  watch: {
47
95
  showLdap(neu, old) {
48
96
  if (neu && !this.model.openLdapConfig) {
49
- const config = {
50
- connectionTimeout: 5000,
51
- groupDNAttribute: 'entryDN',
52
- groupMemberMappingAttribute: 'member',
53
- groupMemberUserAttribute: 'entryDN',
54
- groupNameAttribute: 'cn',
55
- groupObjectClass: 'groupOfNames',
56
- groupSearchAttribute: 'cn',
57
- nestedGroupMembershipEnabled: false,
58
- port: 389,
59
- servers: [],
60
- starttls: false,
61
- tls: false,
62
- disabledStatusBitmask: 0,
63
- userLoginAttribute: 'uid',
64
- userMemberAttribute: 'memberOf',
65
- userNameAttribute: 'cn',
66
- userObjectClass: 'inetOrgPerson',
67
- userSearchAttribute: 'uid|sn|givenName'
68
- };
69
-
70
- this.$set(this.model, 'openLdapConfig', config);
97
+ // Use a spread of config, so that if don't make changes to the defaults if the user edits them
98
+ this.$set(this.model, 'openLdapConfig', { ...LDAP_DEFAULTS });
71
99
  }
72
100
  }
73
101
  }
@@ -98,7 +126,9 @@ export default {
98
126
  :disable="disable"
99
127
  :edit="goToEdit"
100
128
  >
101
- <template slot="rows">
129
+ <template
130
+ slot="rows"
131
+ >
102
132
  <tr><td>{{ t(`authConfig.saml.displayName`) }}: </td><td>{{ model.displayNameField }}</td></tr>
103
133
  <tr><td>{{ t(`authConfig.saml.userName`) }}: </td><td>{{ model.userNameField }}</td></tr>
104
134
  <tr><td>{{ t(`authConfig.saml.UID`) }}: </td><td>{{ model.uidField }}</td></tr>
@@ -106,6 +136,47 @@ export default {
106
136
  <tr><td>{{ t(`authConfig.saml.api`) }}: </td><td>{{ model.rancherApiHost }}</td></tr>
107
137
  <tr><td>{{ t(`authConfig.saml.groups`) }}: </td><td>{{ model.groupsField }}</td></tr>
108
138
  </template>
139
+
140
+ <template
141
+ v-if="supportsLDAPSearch"
142
+ slot="footer"
143
+ >
144
+ <Banner
145
+ v-if="showLdap"
146
+ color="success"
147
+ class="banner"
148
+ >
149
+ <div
150
+ class="advanced-ldap-banner"
151
+ >
152
+ <div>{{ t('authConfig.saml.search.on') }}</div>
153
+ <div>
154
+ <a
155
+ class="toggle-btn"
156
+ @click="showLdapDetails = !showLdapDetails"
157
+ >
158
+ <template v-if="showLdapDetails">{{ t('authConfig.saml.search.hide') }}</template>
159
+ <template v-else>{{ t('authConfig.saml.search.show') }}</template>
160
+ </a>
161
+ </div>
162
+ </div>
163
+ </Banner>
164
+ <Banner
165
+ v-else
166
+ color="info"
167
+ >
168
+ {{ t('authConfig.saml.search.off') }}
169
+ </Banner>
170
+
171
+ <table v-if="showLdapDetails && model.openLdapConfig">
172
+ <tr><td>{{ t('authConfig.ldap.hostname.label') }}:</td><td>{{ ldapHosts }}</td></tr>
173
+ <tr><td>{{ t('authConfig.ldap.port') }}:</td><td>{{ model.openLdapConfig.port }}</td></tr>
174
+ <tr><td>{{ t('authConfig.ldap.protocol') }}:</td><td>{{ ldapProtocol }}</td></tr>
175
+ <tr><td>{{ t('authConfig.ldap.serviceAccountDN') }}:</td><td>{{ model.openLdapConfig.serviceAccountDistinguishedName }}</td></tr>
176
+ <tr><td>{{ t('authConfig.ldap.userSearchBase.label') }}:</td><td>{{ model.openLdapConfig.userSearchBase }}</td></tr>
177
+ <tr><td>{{ t('authConfig.ldap.groupSearchBase.label') }}:</td><td>{{ model.openLdapConfig.groupSearchBase }}</td></tr>
178
+ </table>
179
+ </template>
109
180
  </AuthBanner>
110
181
 
111
182
  <hr>
@@ -233,7 +304,26 @@ export default {
233
304
  />
234
305
  </div>
235
306
  </div>
236
- <div v-if="NAME === 'shibboleth'">
307
+ <div
308
+ v-if="!model.enabled"
309
+ class="row"
310
+ >
311
+ <div class="col span-12">
312
+ <Banner color="info">
313
+ <div v-clean-html="t('authConfig.associatedWarning', tArgs, true)" />
314
+ </Banner>
315
+ </div>
316
+ </div>
317
+ <div v-if="supportsLDAPSearch">
318
+ <div class="row">
319
+ <h2>{{ t('authConfig.saml.search.title') }}</h2>
320
+ </div>
321
+ <div class="row">
322
+ <Banner
323
+ label-key="authConfig.saml.search.message"
324
+ color="info"
325
+ />
326
+ </div>
237
327
  <div class="row">
238
328
  <Checkbox
239
329
  v-model="showLdap"
@@ -243,25 +333,15 @@ export default {
243
333
  </div>
244
334
  <div class="row mt-20 mb-20">
245
335
  <config
246
- v-if="showLdap"
336
+ v-if="showLdap && model.openLdapConfig"
247
337
  v-model="model.openLdapConfig"
248
338
  :type="NAME"
249
339
  :mode="mode"
340
+ :is-create="!model.enabled"
250
341
  />
251
342
  </div>
252
343
  </div>
253
344
  </template>
254
- <div
255
- v-if="!model.enabled"
256
- class="row"
257
- >
258
- <div class="col span-12">
259
- <Banner
260
- v-clean-html="t('authConfig.associatedWarning', tArgs, true)"
261
- color="info"
262
- />
263
- </div>
264
- </div>
265
345
  </CruResource>
266
346
  </div>
267
347
  </template>
@@ -274,4 +354,19 @@ export default {
274
354
  margin: 0 3px;
275
355
  }
276
356
  }
357
+
358
+ // Banner shows message and link formatted right aligned
359
+ .advanced-ldap-banner {
360
+ display: flex;
361
+ flex: 1;
362
+
363
+ > :first-child {
364
+ flex: 1;
365
+ }
366
+
367
+ .toggle-btn {
368
+ cursor: pointer;
369
+ user-select: none;
370
+ }
371
+ }
277
372
  </style>