mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	UI: Upgrade Ember data 5.3.2 (and upgrade minor versions of ember-source and ember-cli) (#28798)
				
					
				
			* upgrade ember-data 5.3.2, uninstall legacy compat, upgrade ember-cli, ember-source * use query instead of findAll for auth methods, update tests * set mutableId for kmip * show generated private key data before transitioning to details * update kv metadata test * remove deprecated methods from path help service * add changelog, update readme version matrix * remove toggle template helper
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/28798.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/28798.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:change | ||||||
|  | ui: Upgrade Ember data to v5.3.2 (and minor upgrade of ember-cli, ember-source to v5.8.0) | ||||||
|  | ``` | ||||||
							
								
								
									
										25
									
								
								ui/README.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								ui/README.md
									
									
									
									
									
								
							| @@ -24,20 +24,19 @@ | |||||||
|  |  | ||||||
| This README outlines the details of collaborating on this Ember application. | This README outlines the details of collaborating on this Ember application. | ||||||
|  |  | ||||||
| ## Ember CLI Version Upgrade Matrix | ## Ember Version Upgrade Matrix | ||||||
|  |  | ||||||
| | Vault Version | Ember Version | | Respective versions for `ember-cli`, `ember-source` and `ember-data` for each version of Vault that contains an upgrade. | ||||||
| | ------------- | ------------- | |  | ||||||
| | 1.17.x        | 5.4.2         | | | Vault Version | Ember CLI | Ember Source | Ember Data | | ||||||
| | 1.15.x        | 4.12.0        | | | ------------- | --------- | ------------ | ---------- | | ||||||
| | 1.14.x        | 4.4.0         | | | 1.19.x        | 5.8.0     | 5.8.0        | 5.3.2      | | ||||||
| | 1.13.x        | 4.4.0         | | | 1.17.x        | 5.4.2     | 5.4.0        | 4.12.4     | | ||||||
| | 1.12.x        | 3.28.5        | | | 1.15.x        | 4.12.1    | 4.12.0       | 4.11.3     | | ||||||
| | 1.11.x        | 3.28.5        | | | 1.13.x        | 4.4.0     | 4.4.4        | 4.5.0      | | ||||||
| | 1.10.x        | 3.28.5        | | | 1.11.x        | 3.28.5    | 3.28.10      | 3.28.6     | | ||||||
| | 1.9.x         | 3.22.0        | | | 1.10.x        | 3.24.0    | 3.24.7       | 3.24.0     | | ||||||
| | 1.8.x         | 3.22.0        | | | 1.9.x         | 3.22.0    | 3.22.0       | 3.22.0     | | ||||||
| | 1.7.x         | 3.11.0        | |  | ||||||
|  |  | ||||||
| ## Prerequisites | ## Prerequisites | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,26 +23,21 @@ export default ApplicationAdapter.extend({ | |||||||
|     const isUnauthenticated = snapshotRecordArray?.adapterOptions?.unauthenticated; |     const isUnauthenticated = snapshotRecordArray?.adapterOptions?.unauthenticated; | ||||||
|     // sys/internal/ui/mounts returns the actual value of the system TTL |     // sys/internal/ui/mounts returns the actual value of the system TTL | ||||||
|     // instead of '0' which just indicates the mount is using system defaults |     // instead of '0' which just indicates the mount is using system defaults | ||||||
|     const useMountsEndpoint = snapshotRecordArray?.adapterOptions?.useMountsEndpoint; |     if (isUnauthenticated) { | ||||||
|     if (isUnauthenticated || useMountsEndpoint) { |  | ||||||
|       const url = `/${this.urlPrefix()}/internal/ui/mounts`; |       const url = `/${this.urlPrefix()}/internal/ui/mounts`; | ||||||
|       return this.ajax(url, 'GET', { |       return this.ajax(url, 'GET', { | ||||||
|         unauthenticated: isUnauthenticated, |         unauthenticated: true, | ||||||
|       }) |       }) | ||||||
|         .then((result) => { |         .then((result) => { | ||||||
|           return { |           return { | ||||||
|             data: result.data.auth, |             data: result.data.auth, | ||||||
|           }; |           }; | ||||||
|         }) |         }) | ||||||
|         .catch((e) => { |         .catch(() => { | ||||||
|           if (isUnauthenticated) return { data: {} }; |           return { data: {} }; | ||||||
|  |  | ||||||
|           if (e instanceof AdapterError) { |  | ||||||
|             set(e, 'policyPath', 'sys/internal/ui/mounts'); |  | ||||||
|           } |  | ||||||
|           throw e; |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     // if authenticated, findAll will use GET sys/auth instead | ||||||
|     return this.ajax(this.url(), 'GET').catch((e) => { |     return this.ajax(this.url(), 'GET').catch((e) => { | ||||||
|       if (e instanceof AdapterError) { |       if (e instanceof AdapterError) { | ||||||
|         set(e, 'policyPath', 'sys/auth'); |         set(e, 'policyPath', 'sys/auth'); | ||||||
| @@ -51,6 +46,25 @@ export default ApplicationAdapter.extend({ | |||||||
|     }); |     }); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  |   // findAll makes a network request and supplements the ember-data store with what the API returns. | ||||||
|  |   // after upgrading to ember-data 5.3.2 the store was becoming cluttered with outdated records, so | ||||||
|  |   // use query to refresh the store with each request. this is ideal for list views | ||||||
|  |   query() { | ||||||
|  |     const url = `/${this.urlPrefix()}/internal/ui/mounts`; | ||||||
|  |     return this.ajax(url, 'GET') | ||||||
|  |       .then((result) => { | ||||||
|  |         return { | ||||||
|  |           data: result.data.auth, | ||||||
|  |         }; | ||||||
|  |       }) | ||||||
|  |       .catch((e) => { | ||||||
|  |         if (e instanceof AdapterError) { | ||||||
|  |           set(e, 'policyPath', 'sys/internal/ui/mounts'); | ||||||
|  |         } | ||||||
|  |         throw e; | ||||||
|  |       }); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|   createRecord(store, type, snapshot) { |   createRecord(store, type, snapshot) { | ||||||
|     const serializer = store.serializerFor(type.modelName); |     const serializer = store.serializerFor(type.modelName); | ||||||
|     const data = serializer.serialize(snapshot); |     const data = serializer.serialize(snapshot); | ||||||
|   | |||||||
| @@ -16,9 +16,18 @@ export default BaseAdapter.extend({ | |||||||
|     return this._url(...arguments); |     return this._url(...arguments); | ||||||
|   }, |   }, | ||||||
|   urlForCreateRecord(modelName, snapshot) { |   urlForCreateRecord(modelName, snapshot) { | ||||||
|     return this._url(snapshot.id, modelName, snapshot); |     const id = snapshot.record.mutableId; | ||||||
|  |     return this._url(id, modelName, snapshot); | ||||||
|   }, |   }, | ||||||
|   urlForUpdateRecord() { |   urlForUpdateRecord() { | ||||||
|     return this._url(...arguments); |     return this._url(...arguments); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  |   createRecord(store, type, snapshot) { | ||||||
|  |     return this._super(...arguments).then(() => { | ||||||
|  |       // saving returns a 204, return object with id to please ember-data... | ||||||
|  |       const id = snapshot.record.mutableId; | ||||||
|  |       return { id }; | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -64,6 +64,9 @@ export default Component.extend({ | |||||||
|   ), |   ), | ||||||
|  |  | ||||||
|   actions: { |   actions: { | ||||||
|  |     handleToggle(e) { | ||||||
|  |       set(this.key, 'enterAsText', e.target.checked); | ||||||
|  |     }, | ||||||
|     pickedFile(e) { |     pickedFile(e) { | ||||||
|       const { files } = e.target; |       const { files } = e.target; | ||||||
|       if (!files.length) { |       if (!files.length) { | ||||||
|   | |||||||
| @@ -15,9 +15,7 @@ export default Route.extend({ | |||||||
|  |  | ||||||
|   model(params) { |   model(params) { | ||||||
|     const { path } = params; |     const { path } = params; | ||||||
|     return this.store |     return this.store.query('auth-method', {}).then((modelArray) => { | ||||||
|       .findAll('auth-method', { adapterOptions: { useMountsEndpoint: true } }) |  | ||||||
|       .then((modelArray) => { |  | ||||||
|       const model = modelArray.find((m) => m.id === path); |       const model = modelArray.find((m) => m.id === path); | ||||||
|       if (!model) { |       if (!model) { | ||||||
|         const error = new AdapterError(); |         const error = new AdapterError(); | ||||||
|   | |||||||
| @@ -19,6 +19,6 @@ export default class VaultClusterAccessMethodsRoute extends Route { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   model() { |   model() { | ||||||
|     return this.store.findAll('auth-method'); |     return this.store.query('auth-method', {}); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -86,7 +86,7 @@ export default Route.extend(UnloadModelRoute, { | |||||||
|         // if you haven't saved a config, the API 404s, so create one here to edit and return it |         // if you haven't saved a config, the API 404s, so create one here to edit and return it | ||||||
|         if (e.httpStatus === 404) { |         if (e.httpStatus === 404) { | ||||||
|           config = this.store.createRecord(modelType, { |           config = this.store.createRecord(modelType, { | ||||||
|             id: backend.id, |             mutableId: backend.id, | ||||||
|           }); |           }); | ||||||
|           config.set('backend', backend); |           config.set('backend', backend); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -49,13 +49,6 @@ export default class PathHelpService extends Service { | |||||||
|  |  | ||||||
|     // bust cache in EmberData's model lookup |     // bust cache in EmberData's model lookup | ||||||
|     delete store._modelFactoryCache[modelType]; |     delete store._modelFactoryCache[modelType]; | ||||||
|  |  | ||||||
|     // bust cache in schema service |  | ||||||
|     const schemas = store.getSchemaDefinitionService?.(); |  | ||||||
|     if (schemas) { |  | ||||||
|       delete schemas._relationshipsDefCache[modelType]; |  | ||||||
|       delete schemas._attributesDefCache[modelType]; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ | |||||||
|         name={{concat "useText-" this.elementId}} |         name={{concat "useText-" this.elementId}} | ||||||
|         class="toggle is-success is-small" |         class="toggle is-success is-small" | ||||||
|         checked={{this.key.enterAsText}} |         checked={{this.key.enterAsText}} | ||||||
|         onchange={{action (toggle "enterAsText" this.key)}} |         onchange={{action "handleToggle"}} | ||||||
|       /> |       /> | ||||||
|       <label for={{concat "useText-" this.elementId}} class="has-text-weight-bold is-size-8"> |       <label for={{concat "useText-" this.elementId}} class="has-text-weight-bold is-size-8"> | ||||||
|         Enter as text |         Enter as text | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ export default Route.extend({ | |||||||
|     return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => { |     return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => { | ||||||
|       if (err.httpStatus === 404) { |       if (err.httpStatus === 404) { | ||||||
|         const model = this.store.createRecord('kmip/config'); |         const model = this.store.createRecord('kmip/config'); | ||||||
|         model.set('id', this.secretMountPath.currentPath); |         model.set('mutableId', this.secretMountPath.currentPath); | ||||||
|         return model; |         return model; | ||||||
|       } else { |       } else { | ||||||
|         throw err; |         throw err; | ||||||
|   | |||||||
| @@ -3,6 +3,14 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
|  | {{! private_key is only available after initial save }} | ||||||
|  | {{#if this.generatedKey.privateKey}} | ||||||
|  |   <Page::PkiKeyDetails | ||||||
|  |     @key={{this.generatedKey}} | ||||||
|  |     @canDelete={{this.generatedKey.canDelete}} | ||||||
|  |     @canEdit={{this.generatedKey.canEdit}} | ||||||
|  |   /> | ||||||
|  | {{else}} | ||||||
|   <form {{on "submit" (perform this.save)}}> |   <form {{on "submit" (perform this.save)}}> | ||||||
|     <div class="box is-sideless is-fullwidth is-marginless"> |     <div class="box is-sideless is-fullwidth is-marginless"> | ||||||
|       <MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" /> |       <MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" /> | ||||||
| @@ -62,3 +70,4 @@ | |||||||
|       </div> |       </div> | ||||||
|     {{/if}} |     {{/if}} | ||||||
|   </form> |   </form> | ||||||
|  | {{/if}} | ||||||
| @@ -39,6 +39,8 @@ export default class PkiKeyForm extends Component<Args> { | |||||||
|   @tracked invalidFormAlert = ''; |   @tracked invalidFormAlert = ''; | ||||||
|   @tracked modelValidations: ValidationMap | null = null; |   @tracked modelValidations: ValidationMap | null = null; | ||||||
|  |  | ||||||
|  |   @tracked generatedKey: PkiKeyModel | null = null; | ||||||
|  |  | ||||||
|   @task |   @task | ||||||
|   @waitFor |   @waitFor | ||||||
|   *save(event: Event) { |   *save(event: Event) { | ||||||
| @@ -51,11 +53,15 @@ export default class PkiKeyForm extends Component<Args> { | |||||||
|         this.invalidFormAlert = invalidFormMessage; |         this.invalidFormAlert = invalidFormMessage; | ||||||
|       } |       } | ||||||
|       if (!isValid && isNew) return; |       if (!isValid && isNew) return; | ||||||
|       yield this.args.model.save({ adapterOptions: { import: false } }); |       this.generatedKey = yield this.args.model.save({ adapterOptions: { import: false } }); | ||||||
|       this.flashMessages.success( |       this.flashMessages.success( | ||||||
|         `Successfully ${isNew ? 'generated' : 'updated'} key${keyName ? ` ${keyName}.` : '.'}` |         `Successfully ${isNew ? 'generated' : 'updated'} key${keyName ? ` ${keyName}.` : '.'}` | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|  |       // only transition to details if there is no private_key data to display | ||||||
|  |       if (!this.generatedKey?.privateKey) { | ||||||
|         this.args.onSave(); |         this.args.onSave(); | ||||||
|  |       } | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       this.errorBanner = errorMessage(error); |       this.errorBanner = errorMessage(error); | ||||||
|       this.invalidFormAlert = 'There was an error submitting this form.'; |       this.invalidFormAlert = 'There was an error submitting this form.'; | ||||||
|   | |||||||
| @@ -36,4 +36,10 @@ export default class PkiRolesCreateRoute extends Route { | |||||||
|       { label: 'create' }, |       { label: 'create' }, | ||||||
|     ]; |     ]; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   willTransition() { | ||||||
|  |     // after upgrading to Ember Data 5.3.2 we saw duplicate records in the store after creating and saving a new role | ||||||
|  |     // it's unclear why this ghost record is persisting, manually unloading refreshes the store | ||||||
|  |     this.store.unloadAll('pki/role'); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -66,7 +66,6 @@ | |||||||
|     "@babel/preset-env": "^7.24.6", |     "@babel/preset-env": "^7.24.6", | ||||||
|     "@babel/preset-typescript": "^7.24.6", |     "@babel/preset-typescript": "^7.24.6", | ||||||
|     "@docfy/ember": "^0.8.5", |     "@docfy/ember": "^0.8.5", | ||||||
|     "@ember-data/legacy-compat": "~4.12.4", |  | ||||||
|     "@ember/legacy-built-in-components": "^0.4.1", |     "@ember/legacy-built-in-components": "^0.4.1", | ||||||
|     "@ember/optional-features": "^2.0.0", |     "@ember/optional-features": "^2.0.0", | ||||||
|     "@ember/render-modifiers": "^1.0.2", |     "@ember/render-modifiers": "^1.0.2", | ||||||
| @@ -106,7 +105,7 @@ | |||||||
|     "dompurify": "^3.0.2", |     "dompurify": "^3.0.2", | ||||||
|     "ember-a11y-testing": "^7.0.1", |     "ember-a11y-testing": "^7.0.1", | ||||||
|     "ember-basic-dropdown": "^8.0.4", |     "ember-basic-dropdown": "^8.0.4", | ||||||
|     "ember-cli": "~5.4.2", |     "ember-cli": "~5.8.0", | ||||||
|     "ember-cli-babel": "^8.2.0", |     "ember-cli-babel": "^8.2.0", | ||||||
|     "ember-cli-clean-css": "^3.0.0", |     "ember-cli-clean-css": "^3.0.0", | ||||||
|     "ember-cli-content-security-policy": "2.0.3", |     "ember-cli-content-security-policy": "2.0.3", | ||||||
| @@ -123,7 +122,7 @@ | |||||||
|     "ember-cli-terser": "^4.0.2", |     "ember-cli-terser": "^4.0.2", | ||||||
|     "ember-composable-helpers": "5.0.0", |     "ember-composable-helpers": "5.0.0", | ||||||
|     "ember-concurrency": "^4.0.2", |     "ember-concurrency": "^4.0.2", | ||||||
|     "ember-data": "~4.12.4", |     "ember-data": "~5.3.2", | ||||||
|     "ember-engines": "0.8.23", |     "ember-engines": "0.8.23", | ||||||
|     "ember-exam": "^9.0.0", |     "ember-exam": "^9.0.0", | ||||||
|     "ember-inflector": "4.0.2", |     "ember-inflector": "4.0.2", | ||||||
| @@ -138,7 +137,7 @@ | |||||||
|     "ember-responsive": "5.0.0", |     "ember-responsive": "5.0.0", | ||||||
|     "ember-service-worker": "meirish/ember-service-worker#configurable-scope", |     "ember-service-worker": "meirish/ember-service-worker#configurable-scope", | ||||||
|     "ember-sinon-qunit": "^7.4.0", |     "ember-sinon-qunit": "^7.4.0", | ||||||
|     "ember-source": "~5.4.0", |     "ember-source": "~5.8.0", | ||||||
|     "ember-style-modifier": "^4.1.0", |     "ember-style-modifier": "^4.1.0", | ||||||
|     "ember-svg-jar": "2.4.4", |     "ember-svg-jar": "2.4.4", | ||||||
|     "ember-template-lint": "^6.0.0", |     "ember-template-lint": "^6.0.0", | ||||||
|   | |||||||
| @@ -3,22 +3,17 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import { currentRouteName, click } from '@ember/test-helpers'; | import { currentRouteName, click, find, findAll, visit } from '@ember/test-helpers'; | ||||||
| import { clickTrigger } from 'ember-power-select/test-support/helpers'; | import { clickTrigger } from 'ember-power-select/test-support/helpers'; | ||||||
| import { module, test } from 'qunit'; | import { module, test } from 'qunit'; | ||||||
| import { setupApplicationTest } from 'ember-qunit'; | import { setupApplicationTest } from 'ember-qunit'; | ||||||
| import { setupMirage } from 'ember-cli-mirage/test-support'; | import { setupMirage } from 'ember-cli-mirage/test-support'; | ||||||
| import { create } from 'ember-cli-page-object'; |  | ||||||
| import page from 'vault/tests/pages/access/methods'; |  | ||||||
| import authEnable from 'vault/tests/pages/settings/auth/enable'; |  | ||||||
| import authPage from 'vault/tests/pages/auth'; |  | ||||||
| import ss from 'vault/tests/pages/components/search-select'; |  | ||||||
| import consoleClass from 'vault/tests/pages/components/console/ui-panel'; |  | ||||||
|  |  | ||||||
| import { v4 as uuidv4 } from 'uuid'; | import { v4 as uuidv4 } from 'uuid'; | ||||||
|  | import { GENERAL } from 'vault/tests/helpers/general-selectors'; | ||||||
|  | import { mountAuthCmd, runCmd } from 'vault/tests/helpers/commands'; | ||||||
|  | import { login } from 'vault/tests/helpers/auth/auth-helpers'; | ||||||
|  |  | ||||||
| const consoleComponent = create(consoleClass); | const { searchSelect } = GENERAL; | ||||||
| const searchSelect = create(ss); |  | ||||||
|  |  | ||||||
| module('Acceptance | auth-methods list view', function (hooks) { | module('Acceptance | auth-methods list view', function (hooks) { | ||||||
|   setupApplicationTest(hooks); |   setupApplicationTest(hooks); | ||||||
| @@ -26,14 +21,13 @@ module('Acceptance | auth-methods list view', function (hooks) { | |||||||
|  |  | ||||||
|   hooks.beforeEach(function () { |   hooks.beforeEach(function () { | ||||||
|     this.uid = uuidv4(); |     this.uid = uuidv4(); | ||||||
|     return authPage.login(); |     return login(); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it navigates to auth method', async function (assert) { |   test('it navigates to auth method', async function (assert) { | ||||||
|     await page.visit(); |     await visit('/vault/access/'); | ||||||
|     assert.strictEqual(currentRouteName(), 'vault.cluster.access.methods', 'navigates to the correct route'); |     assert.strictEqual(currentRouteName(), 'vault.cluster.access.methods', 'navigates to the correct route'); | ||||||
|     assert.ok(page.methodsLink.isActive, 'the first link is active'); |     assert.dom('[data-test-sidebar-nav-link="Authentication Methods"]').hasClass('active'); | ||||||
|     assert.strictEqual(page.methodsLink.text, 'Authentication Methods'); |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it filters by name and auth type', async function (assert) { |   test('it filters by name and auth type', async function (assert) { | ||||||
| @@ -41,50 +35,52 @@ module('Acceptance | auth-methods list view', function (hooks) { | |||||||
|     const authPath1 = `userpass-1-${this.uid}`; |     const authPath1 = `userpass-1-${this.uid}`; | ||||||
|     const authPath2 = `userpass-2-${this.uid}`; |     const authPath2 = `userpass-2-${this.uid}`; | ||||||
|     const type = 'userpass'; |     const type = 'userpass'; | ||||||
|     await authEnable.visit(); |     await visit('/vault/settings/auth/enable'); | ||||||
|     await authEnable.enable(type, authPath1); |     await runCmd(mountAuthCmd(type, authPath1)); | ||||||
|     await authEnable.visit(); |     await visit('/vault/settings/auth/enable'); | ||||||
|     await authEnable.enable(type, authPath2); |     await runCmd(mountAuthCmd(type, authPath2)); | ||||||
|     await page.visit(); |     await visit('/vault/access/'); | ||||||
|     // filter by auth type |  | ||||||
|  |  | ||||||
|  |     // filter by auth type | ||||||
|     await clickTrigger('#filter-by-auth-type'); |     await clickTrigger('#filter-by-auth-type'); | ||||||
|     await searchSelect.options.objectAt(0).click(); |     await click(searchSelect.option(searchSelect.optionIndex(type))); | ||||||
|     const rows = document.querySelectorAll('[data-test-auth-backend-link]'); |     let rows = findAll('[data-test-auth-backend-link]'); | ||||||
|     const rowsUserpass = Array.from(rows).filter((row) => row.innerText.includes('userpass')); |     const rowsUserpass = Array.from(rows).filter((row) => row.innerText.includes('userpass')); | ||||||
|  |  | ||||||
|     assert.strictEqual(rows.length, rowsUserpass.length, 'all rows returned are userpass'); |     assert.strictEqual(rows.length, rowsUserpass.length, 'all rows returned are userpass'); | ||||||
|  |  | ||||||
|     // filter by name |     // filter by name | ||||||
|     await clickTrigger('#filter-by-auth-name'); |     await clickTrigger('#filter-by-auth-name'); | ||||||
|     const firstItemToSelect = searchSelect.options.objectAt(0).text; |     await click(searchSelect.option()); | ||||||
|     await searchSelect.options.objectAt(0).click(); |     const selectedItem = find(`#filter-by-auth-name ${searchSelect.selectedOption()}`).innerText; | ||||||
|     const singleRow = document.querySelectorAll('[data-test-auth-backend-link]'); |     const singleRow = findAll('[data-test-auth-backend-link]'); | ||||||
|  |  | ||||||
|     assert.strictEqual(singleRow.length, 1, 'returns only one row'); |     assert.strictEqual(singleRow.length, 1, 'returns only one row'); | ||||||
|     assert.dom(singleRow[0]).includesText(firstItemToSelect, 'shows the filtered by auth name'); |     assert.dom(singleRow[0]).includesText(selectedItem, 'shows the filtered by auth name'); | ||||||
|     // clear filter by engine name |     // clear filter by name | ||||||
|     await searchSelect.deleteButtons.objectAt(1).click(); |     await click(`#filter-by-auth-name ${searchSelect.removeSelected}`); | ||||||
|     const rowsAgain = document.querySelectorAll('[data-test-auth-backend-link]'); |     rows = findAll('[data-test-auth-backend-link]'); | ||||||
|     assert.ok(rowsAgain.length > 1, 'filter has been removed'); |     assert.true(rows.length > 1, 'filter has been removed'); | ||||||
|  |  | ||||||
|     // cleanup |     // cleanup | ||||||
|     await consoleComponent.runCommands([`delete sys/auth/${authPath1}`]); |     await runCmd(`delete sys/auth/${authPath1}`); | ||||||
|     await consoleComponent.runCommands([`delete sys/auth/${authPath2}`]); |     await runCmd(`delete sys/auth/${authPath2}`); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it should show all methods in list view', async function (assert) { |   test('it should show all methods in list view', async function (assert) { | ||||||
|     this.server.get('/sys/auth', () => ({ |     this.server.get('/sys/internal/ui/mounts', () => ({ | ||||||
|       data: { |       data: { | ||||||
|  |         auth: { | ||||||
|           'token/': { accessor: 'auth_token_263b8b4e', type: 'token' }, |           'token/': { accessor: 'auth_token_263b8b4e', type: 'token' }, | ||||||
|           'userpass/': { accessor: 'auth_userpass_87aca1f8', type: 'userpass' }, |           'userpass/': { accessor: 'auth_userpass_87aca1f8', type: 'userpass' }, | ||||||
|         }, |         }, | ||||||
|  |       }, | ||||||
|     })); |     })); | ||||||
|     await page.visit(); |     await visit('/vault/access/'); | ||||||
|     assert.dom('[data-test-auth-backend-link]').exists({ count: 2 }, 'All auth methods appear in list view'); |     assert.dom('[data-test-auth-backend-link]').exists({ count: 2 }, 'All auth methods appear in list view'); | ||||||
|     await authEnable.visit(); |     await visit('/vault/settings/auth/enable'); | ||||||
|     await click('[data-test-sidebar-nav-link="OIDC Provider"]'); |     await click('[data-test-sidebar-nav-link="OIDC Provider"]'); | ||||||
|     await page.visit(); |     await visit('/vault/access/'); | ||||||
|     assert |     assert | ||||||
|       .dom('[data-test-auth-backend-link]') |       .dom('[data-test-auth-backend-link]') | ||||||
|       .exists({ count: 2 }, 'All auth methods appear in list view after navigating back'); |       .exists({ count: 2 }, 'All auth methods appear in list view after navigating back'); | ||||||
|   | |||||||
| @@ -307,11 +307,15 @@ module('Acceptance | pki workflow', function (hooks) { | |||||||
|       await visit(`/vault/secrets/${this.mountPath}/pki/keys`); |       await visit(`/vault/secrets/${this.mountPath}/pki/keys`); | ||||||
|       await click(PKI_KEYS.generateKey); |       await click(PKI_KEYS.generateKey); | ||||||
|       assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/create`); |       assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/create`); | ||||||
|       await fillIn(GENERAL.inputByAttr('type'), 'exported'); |       await fillIn(GENERAL.inputByAttr('type'), 'exported'); // exported keys generated private_key data | ||||||
|       await fillIn(GENERAL.inputByAttr('keyType'), 'rsa'); |       await fillIn(GENERAL.inputByAttr('keyType'), 'rsa'); | ||||||
|       await click(GENERAL.saveButton); |       await click(GENERAL.saveButton); | ||||||
|       keyId = find(GENERAL.infoRowValue('Key ID')).textContent?.trim(); |       keyId = find(GENERAL.infoRowValue('Key ID')).textContent?.trim(); | ||||||
|       assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`); |       assert.strictEqual( | ||||||
|  |         currentURL(), | ||||||
|  |         `/vault/secrets/${this.mountPath}/pki/keys/create`, | ||||||
|  |         'it does not transition to details private_key data exists' | ||||||
|  |       ); | ||||||
|  |  | ||||||
|       assert |       assert | ||||||
|         .dom(PKI_KEYS.nextStepsAlert) |         .dom(PKI_KEYS.nextStepsAlert) | ||||||
|   | |||||||
| @@ -3,15 +3,14 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import { click, currentRouteName, settled } from '@ember/test-helpers'; | import { click, currentRouteName, fillIn, visit } from '@ember/test-helpers'; | ||||||
| import { module, test } from 'qunit'; | import { module, test } from 'qunit'; | ||||||
| import { setupApplicationTest } from 'ember-qunit'; | import { setupApplicationTest } from 'ember-qunit'; | ||||||
| import { v4 as uuidv4 } from 'uuid'; | import { v4 as uuidv4 } from 'uuid'; | ||||||
|  |  | ||||||
| import { GENERAL } from 'vault/tests/helpers/general-selectors'; | import { GENERAL } from 'vault/tests/helpers/general-selectors'; | ||||||
| import page from 'vault/tests/pages/settings/auth/enable'; |  | ||||||
| import listPage from 'vault/tests/pages/access/methods'; |  | ||||||
| import { login } from 'vault/tests/helpers/auth/auth-helpers'; | import { login } from 'vault/tests/helpers/auth/auth-helpers'; | ||||||
|  | import { deleteAuthCmd, runCmd } from 'vault/tests/helpers/commands'; | ||||||
|  | import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors'; | ||||||
|  |  | ||||||
| module('Acceptance | settings/auth/enable', function (hooks) { | module('Acceptance | settings/auth/enable', function (hooks) { | ||||||
|   setupApplicationTest(hooks); |   setupApplicationTest(hooks); | ||||||
| @@ -25,30 +24,34 @@ module('Acceptance | settings/auth/enable', function (hooks) { | |||||||
|     // always force the new mount to the top of the list |     // always force the new mount to the top of the list | ||||||
|     const path = `aaa-approle-${this.uid}`; |     const path = `aaa-approle-${this.uid}`; | ||||||
|     const type = 'approle'; |     const type = 'approle'; | ||||||
|     await page.visit(); |     await visit('/vault/settings/auth/enable'); | ||||||
|     assert.strictEqual(currentRouteName(), 'vault.cluster.settings.auth.enable'); |     assert.strictEqual(currentRouteName(), 'vault.cluster.settings.auth.enable'); | ||||||
|     await page.enable(type, path); |     await click(SES.mountType(type)); | ||||||
|     await settled(); |     await fillIn(GENERAL.inputByAttr('path'), path); | ||||||
|     assert.strictEqual( |     await click(SES.mountSubmit); | ||||||
|       page.flash.latestMessage, |     assert | ||||||
|       `Successfully mounted the ${type} auth method at ${path}.`, |       .dom(GENERAL.latestFlashContent) | ||||||
|       'success flash shows' |       .hasText(`Successfully mounted the ${type} auth method at ${path}.`); | ||||||
|     ); |  | ||||||
|     assert.strictEqual( |     assert.strictEqual( | ||||||
|       currentRouteName(), |       currentRouteName(), | ||||||
|       'vault.cluster.settings.auth.configure.section', |       'vault.cluster.settings.auth.configure.section', | ||||||
|       'redirects to the auth config page' |       'redirects to the auth config page' | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     await listPage.visit(); |     await visit('/vault/access/'); | ||||||
|     assert.ok(listPage.findLinkById(path), 'mount is present in the list'); |     assert.dom(`[data-test-auth-backend-link=${path}]`).exists('mount is present in the list'); | ||||||
|  |  | ||||||
|  |     // cleanup | ||||||
|  |     await runCmd(deleteAuthCmd(path)); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it renders default config details', async function (assert) { |   test('it renders default config details', async function (assert) { | ||||||
|     const path = `approle-config-${this.uid}`; |     const path = `approle-config-${this.uid}`; | ||||||
|     const type = 'approle'; |     const type = 'approle'; | ||||||
|     await page.visit(); |     await visit('/vault/settings/auth/enable'); | ||||||
|     await page.enable(type, path); |     await click(SES.mountType(type)); | ||||||
|  |     await fillIn(GENERAL.inputByAttr('path'), path); | ||||||
|  |     await click(SES.mountSubmit); | ||||||
|     // the config details is updated to query mount details from sys/internal/ui/mounts |     // the config details is updated to query mount details from sys/internal/ui/mounts | ||||||
|     // but we still want these forms to continue using sys/auth which returns 0 for default ttl values |     // but we still want these forms to continue using sys/auth which returns 0 for default ttl values | ||||||
|     // check tune form (right after enabling) |     // check tune form (right after enabling) | ||||||
| @@ -64,5 +67,8 @@ module('Acceptance | settings/auth/enable', function (hooks) { | |||||||
|     await click('[data-test-configure-link]'); |     await click('[data-test-configure-link]'); | ||||||
|     assert.dom(GENERAL.toggleInput('Default Lease TTL')).isNotChecked('default lease ttl is still unset'); |     assert.dom(GENERAL.toggleInput('Default Lease TTL')).isNotChecked('default lease ttl is still unset'); | ||||||
|     assert.dom(GENERAL.toggleInput('Max Lease TTL')).isNotChecked('max lease ttl is still unset'); |     assert.dom(GENERAL.toggleInput('Max Lease TTL')).isNotChecked('max lease ttl is still unset'); | ||||||
|  |  | ||||||
|  |     // cleanup | ||||||
|  |     await runCmd(deleteAuthCmd(path)); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -1,24 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import { create, attribute, visitable, collection, hasClass, text } from 'ember-cli-page-object'; |  | ||||||
|  |  | ||||||
| export default create({ |  | ||||||
|   visit: visitable('/vault/access/'), |  | ||||||
|   methodsLink: { |  | ||||||
|     isActive: hasClass('active'), |  | ||||||
|     text: text(), |  | ||||||
|     scope: '[data-test-sidebar-nav-link="Authentication Methods"]', |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   backendLinks: collection('[data-test-auth-backend-link]', { |  | ||||||
|     path: text('[data-test-path]'), |  | ||||||
|     id: attribute('data-test-id', '[data-test-path]'), |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   findLinkById(id) { |  | ||||||
|     return this.backendLinks.filterBy('id', id)[0]; |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -54,7 +54,7 @@ module('Unit | Adapter | auth method', function (hooks) { | |||||||
|     await this.store.findAll('auth-method', { adapterOptions: { unauthenticated: true } }); |     await this.store.findAll('auth-method', { adapterOptions: { unauthenticated: true } }); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('findAll makes request to correct endpoint when useMountsEndpoint is true', async function (assert) { |   test('query makes request to correct endpoint ', async function (assert) { | ||||||
|     assert.expect(1); |     assert.expect(1); | ||||||
|  |  | ||||||
|     this.server.get('sys/internal/ui/mounts', () => { |     this.server.get('sys/internal/ui/mounts', () => { | ||||||
| @@ -62,6 +62,6 @@ module('Unit | Adapter | auth method', function (hooks) { | |||||||
|       return this.mockResponse; |       return this.mockResponse; | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     await this.store.findAll('auth-method', { adapterOptions: { useMountsEndpoint: true } }); |     await this.store.query('auth-method', {}); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -182,7 +182,7 @@ module('Unit | Adapter | kv/metadata', function (hooks) { | |||||||
|     let record = await this.store.peekRecord('kv/metadata', data.id); |     let record = await this.store.peekRecord('kv/metadata', data.id); | ||||||
|  |  | ||||||
|     await record.destroyRecord(); |     await record.destroyRecord(); | ||||||
|     assert.true(record.isDestroyed, 'record is destroyed'); |     assert.true(record.isDeleted, 'record is deleted'); | ||||||
|     record = await this.store.peekRecord('kv/metadata', this.id); |     record = await this.store.peekRecord('kv/metadata', this.id); | ||||||
|     assert.strictEqual(record, null, 'record is no longer in store'); |     assert.strictEqual(record, null, 'record is no longer in store'); | ||||||
|   }); |   }); | ||||||
|   | |||||||
							
								
								
									
										898
									
								
								ui/yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										898
									
								
								ui/yarn.lock
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user
	 claire bontempo
					claire bontempo