mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 01:32:33 +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.
|
||||
|
||||
## Ember CLI Version Upgrade Matrix
|
||||
## Ember Version Upgrade Matrix
|
||||
|
||||
| Vault Version | Ember Version |
|
||||
| ------------- | ------------- |
|
||||
| 1.17.x | 5.4.2 |
|
||||
| 1.15.x | 4.12.0 |
|
||||
| 1.14.x | 4.4.0 |
|
||||
| 1.13.x | 4.4.0 |
|
||||
| 1.12.x | 3.28.5 |
|
||||
| 1.11.x | 3.28.5 |
|
||||
| 1.10.x | 3.28.5 |
|
||||
| 1.9.x | 3.22.0 |
|
||||
| 1.8.x | 3.22.0 |
|
||||
| 1.7.x | 3.11.0 |
|
||||
Respective versions for `ember-cli`, `ember-source` and `ember-data` for each version of Vault that contains an upgrade.
|
||||
|
||||
| Vault Version | Ember CLI | Ember Source | Ember Data |
|
||||
| ------------- | --------- | ------------ | ---------- |
|
||||
| 1.19.x | 5.8.0 | 5.8.0 | 5.3.2 |
|
||||
| 1.17.x | 5.4.2 | 5.4.0 | 4.12.4 |
|
||||
| 1.15.x | 4.12.1 | 4.12.0 | 4.11.3 |
|
||||
| 1.13.x | 4.4.0 | 4.4.4 | 4.5.0 |
|
||||
| 1.11.x | 3.28.5 | 3.28.10 | 3.28.6 |
|
||||
| 1.10.x | 3.24.0 | 3.24.7 | 3.24.0 |
|
||||
| 1.9.x | 3.22.0 | 3.22.0 | 3.22.0 |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -23,26 +23,21 @@ export default ApplicationAdapter.extend({
|
||||
const isUnauthenticated = snapshotRecordArray?.adapterOptions?.unauthenticated;
|
||||
// sys/internal/ui/mounts returns the actual value of the system TTL
|
||||
// instead of '0' which just indicates the mount is using system defaults
|
||||
const useMountsEndpoint = snapshotRecordArray?.adapterOptions?.useMountsEndpoint;
|
||||
if (isUnauthenticated || useMountsEndpoint) {
|
||||
if (isUnauthenticated) {
|
||||
const url = `/${this.urlPrefix()}/internal/ui/mounts`;
|
||||
return this.ajax(url, 'GET', {
|
||||
unauthenticated: isUnauthenticated,
|
||||
unauthenticated: true,
|
||||
})
|
||||
.then((result) => {
|
||||
return {
|
||||
data: result.data.auth,
|
||||
};
|
||||
})
|
||||
.catch((e) => {
|
||||
if (isUnauthenticated) return { data: {} };
|
||||
|
||||
if (e instanceof AdapterError) {
|
||||
set(e, 'policyPath', 'sys/internal/ui/mounts');
|
||||
}
|
||||
throw e;
|
||||
.catch(() => {
|
||||
return { data: {} };
|
||||
});
|
||||
}
|
||||
// if authenticated, findAll will use GET sys/auth instead
|
||||
return this.ajax(this.url(), 'GET').catch((e) => {
|
||||
if (e instanceof AdapterError) {
|
||||
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) {
|
||||
const serializer = store.serializerFor(type.modelName);
|
||||
const data = serializer.serialize(snapshot);
|
||||
|
||||
@@ -16,9 +16,18 @@ export default BaseAdapter.extend({
|
||||
return this._url(...arguments);
|
||||
},
|
||||
urlForCreateRecord(modelName, snapshot) {
|
||||
return this._url(snapshot.id, modelName, snapshot);
|
||||
const id = snapshot.record.mutableId;
|
||||
return this._url(id, modelName, snapshot);
|
||||
},
|
||||
urlForUpdateRecord() {
|
||||
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: {
|
||||
handleToggle(e) {
|
||||
set(this.key, 'enterAsText', e.target.checked);
|
||||
},
|
||||
pickedFile(e) {
|
||||
const { files } = e.target;
|
||||
if (!files.length) {
|
||||
|
||||
@@ -15,28 +15,26 @@ export default Route.extend({
|
||||
|
||||
model(params) {
|
||||
const { path } = params;
|
||||
return this.store
|
||||
.findAll('auth-method', { adapterOptions: { useMountsEndpoint: true } })
|
||||
.then((modelArray) => {
|
||||
const model = modelArray.find((m) => m.id === path);
|
||||
if (!model) {
|
||||
const error = new AdapterError();
|
||||
set(error, 'httpStatus', 404);
|
||||
throw error;
|
||||
}
|
||||
const supportManaged = supportedManagedAuthBackends();
|
||||
if (!supportManaged.includes(model.methodType)) {
|
||||
// do not fetch path-help for unmanaged auth types
|
||||
model.set('paths', {
|
||||
apiPath: model.apiPath,
|
||||
paths: [],
|
||||
});
|
||||
return model;
|
||||
}
|
||||
return this.pathHelp.getPaths(model.apiPath, path).then((paths) => {
|
||||
model.set('paths', paths);
|
||||
return model;
|
||||
return this.store.query('auth-method', {}).then((modelArray) => {
|
||||
const model = modelArray.find((m) => m.id === path);
|
||||
if (!model) {
|
||||
const error = new AdapterError();
|
||||
set(error, 'httpStatus', 404);
|
||||
throw error;
|
||||
}
|
||||
const supportManaged = supportedManagedAuthBackends();
|
||||
if (!supportManaged.includes(model.methodType)) {
|
||||
// do not fetch path-help for unmanaged auth types
|
||||
model.set('paths', {
|
||||
apiPath: model.apiPath,
|
||||
paths: [],
|
||||
});
|
||||
return model;
|
||||
}
|
||||
return this.pathHelp.getPaths(model.apiPath, path).then((paths) => {
|
||||
model.set('paths', paths);
|
||||
return model;
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -19,6 +19,6 @@ export default class VaultClusterAccessMethodsRoute extends Route {
|
||||
};
|
||||
|
||||
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 (e.httpStatus === 404) {
|
||||
config = this.store.createRecord(modelType, {
|
||||
id: backend.id,
|
||||
mutableId: backend.id,
|
||||
});
|
||||
config.set('backend', backend);
|
||||
|
||||
|
||||
@@ -49,13 +49,6 @@ export default class PathHelpService extends Service {
|
||||
|
||||
// bust cache in EmberData's model lookup
|
||||
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}}
|
||||
class="toggle is-success is-small"
|
||||
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">
|
||||
Enter as text
|
||||
|
||||
@@ -17,7 +17,7 @@ export default Route.extend({
|
||||
return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => {
|
||||
if (err.httpStatus === 404) {
|
||||
const model = this.store.createRecord('kmip/config');
|
||||
model.set('id', this.secretMountPath.currentPath);
|
||||
model.set('mutableId', this.secretMountPath.currentPath);
|
||||
return model;
|
||||
} else {
|
||||
throw err;
|
||||
|
||||
@@ -3,62 +3,71 @@
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<form {{on "submit" (perform this.save)}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
|
||||
<NamespaceReminder @mode={{if @model.isNew "generate" "update"}} @noun="PKI key" />
|
||||
{{#if @model.isNew}}
|
||||
{{#each @model.formFieldGroups as |fieldGroup|}}
|
||||
{{#each-in fieldGroup as |group fields|}}
|
||||
{{#if (eq group "Key parameters")}}
|
||||
<PkiKeyParameters @model={{@model}} @fields={{fields}} @modelValidations={{this.modelValidations}} />
|
||||
{{else}}
|
||||
{{#each fields as |attr|}}
|
||||
<FormField
|
||||
data-test-field={{attr}}
|
||||
@attr={{attr}}
|
||||
@model={{@model}}
|
||||
@modelValidations={{this.modelValidations}}
|
||||
@showHelpText={{false}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/each-in}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{! only key name is edit-able }}
|
||||
{{#let (find-by "name" "keyName" @model.formFields) as |keyName|}}
|
||||
<FormField data-test-field={{keyName}} @attr={{keyName}} @model={{@model}} @showHelpText={{false}} />
|
||||
{{/let}}
|
||||
{{#let (find-by "name" "keyType" @model.formFields) as |keyType|}}
|
||||
<ReadonlyFormField @attr={{keyType}} @value={{@model.keyType}} />
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<Hds::ButtonSet class="has-top-padding-s">
|
||||
<Hds::Button
|
||||
@text={{if @model.isNew "Generate key" "Edit key"}}
|
||||
@icon={{if this.save.isRunning "loading"}}
|
||||
type="submit"
|
||||
disabled={{this.save.isRunning}}
|
||||
data-test-save
|
||||
/>
|
||||
<Hds::Button
|
||||
@text="Cancel"
|
||||
@color="secondary"
|
||||
disabled={{this.save.isRunning}}
|
||||
{{on "click" @onCancel}}
|
||||
data-test-cancel
|
||||
/>
|
||||
</Hds::ButtonSet>
|
||||
{{#if this.invalidFormAlert}}
|
||||
<div class="control">
|
||||
<AlertInline
|
||||
@type="danger"
|
||||
class="has-top-padding-s"
|
||||
@message={{this.invalidFormAlert}}
|
||||
data-test-pki-key-validation-error
|
||||
/>
|
||||
{{! 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)}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
|
||||
<NamespaceReminder @mode={{if @model.isNew "generate" "update"}} @noun="PKI key" />
|
||||
{{#if @model.isNew}}
|
||||
{{#each @model.formFieldGroups as |fieldGroup|}}
|
||||
{{#each-in fieldGroup as |group fields|}}
|
||||
{{#if (eq group "Key parameters")}}
|
||||
<PkiKeyParameters @model={{@model}} @fields={{fields}} @modelValidations={{this.modelValidations}} />
|
||||
{{else}}
|
||||
{{#each fields as |attr|}}
|
||||
<FormField
|
||||
data-test-field={{attr}}
|
||||
@attr={{attr}}
|
||||
@model={{@model}}
|
||||
@modelValidations={{this.modelValidations}}
|
||||
@showHelpText={{false}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/each-in}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{! only key name is edit-able }}
|
||||
{{#let (find-by "name" "keyName" @model.formFields) as |keyName|}}
|
||||
<FormField data-test-field={{keyName}} @attr={{keyName}} @model={{@model}} @showHelpText={{false}} />
|
||||
{{/let}}
|
||||
{{#let (find-by "name" "keyType" @model.formFields) as |keyType|}}
|
||||
<ReadonlyFormField @attr={{keyType}} @value={{@model.keyType}} />
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
<Hds::ButtonSet class="has-top-padding-s">
|
||||
<Hds::Button
|
||||
@text={{if @model.isNew "Generate key" "Edit key"}}
|
||||
@icon={{if this.save.isRunning "loading"}}
|
||||
type="submit"
|
||||
disabled={{this.save.isRunning}}
|
||||
data-test-save
|
||||
/>
|
||||
<Hds::Button
|
||||
@text="Cancel"
|
||||
@color="secondary"
|
||||
disabled={{this.save.isRunning}}
|
||||
{{on "click" @onCancel}}
|
||||
data-test-cancel
|
||||
/>
|
||||
</Hds::ButtonSet>
|
||||
{{#if this.invalidFormAlert}}
|
||||
<div class="control">
|
||||
<AlertInline
|
||||
@type="danger"
|
||||
class="has-top-padding-s"
|
||||
@message={{this.invalidFormAlert}}
|
||||
data-test-pki-key-validation-error
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
{{/if}}
|
||||
@@ -39,6 +39,8 @@ export default class PkiKeyForm extends Component<Args> {
|
||||
@tracked invalidFormAlert = '';
|
||||
@tracked modelValidations: ValidationMap | null = null;
|
||||
|
||||
@tracked generatedKey: PkiKeyModel | null = null;
|
||||
|
||||
@task
|
||||
@waitFor
|
||||
*save(event: Event) {
|
||||
@@ -51,11 +53,15 @@ export default class PkiKeyForm extends Component<Args> {
|
||||
this.invalidFormAlert = invalidFormMessage;
|
||||
}
|
||||
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(
|
||||
`Successfully ${isNew ? 'generated' : 'updated'} key${keyName ? ` ${keyName}.` : '.'}`
|
||||
);
|
||||
this.args.onSave();
|
||||
|
||||
// only transition to details if there is no private_key data to display
|
||||
if (!this.generatedKey?.privateKey) {
|
||||
this.args.onSave();
|
||||
}
|
||||
} catch (error) {
|
||||
this.errorBanner = errorMessage(error);
|
||||
this.invalidFormAlert = 'There was an error submitting this form.';
|
||||
|
||||
@@ -36,4 +36,10 @@ export default class PkiRolesCreateRoute extends Route {
|
||||
{ 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-typescript": "^7.24.6",
|
||||
"@docfy/ember": "^0.8.5",
|
||||
"@ember-data/legacy-compat": "~4.12.4",
|
||||
"@ember/legacy-built-in-components": "^0.4.1",
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/render-modifiers": "^1.0.2",
|
||||
@@ -106,7 +105,7 @@
|
||||
"dompurify": "^3.0.2",
|
||||
"ember-a11y-testing": "^7.0.1",
|
||||
"ember-basic-dropdown": "^8.0.4",
|
||||
"ember-cli": "~5.4.2",
|
||||
"ember-cli": "~5.8.0",
|
||||
"ember-cli-babel": "^8.2.0",
|
||||
"ember-cli-clean-css": "^3.0.0",
|
||||
"ember-cli-content-security-policy": "2.0.3",
|
||||
@@ -123,7 +122,7 @@
|
||||
"ember-cli-terser": "^4.0.2",
|
||||
"ember-composable-helpers": "5.0.0",
|
||||
"ember-concurrency": "^4.0.2",
|
||||
"ember-data": "~4.12.4",
|
||||
"ember-data": "~5.3.2",
|
||||
"ember-engines": "0.8.23",
|
||||
"ember-exam": "^9.0.0",
|
||||
"ember-inflector": "4.0.2",
|
||||
@@ -138,7 +137,7 @@
|
||||
"ember-responsive": "5.0.0",
|
||||
"ember-service-worker": "meirish/ember-service-worker#configurable-scope",
|
||||
"ember-sinon-qunit": "^7.4.0",
|
||||
"ember-source": "~5.4.0",
|
||||
"ember-source": "~5.8.0",
|
||||
"ember-style-modifier": "^4.1.0",
|
||||
"ember-svg-jar": "2.4.4",
|
||||
"ember-template-lint": "^6.0.0",
|
||||
|
||||
@@ -3,22 +3,17 @@
|
||||
* 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 { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
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 { 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 = create(ss);
|
||||
const { searchSelect } = GENERAL;
|
||||
|
||||
module('Acceptance | auth-methods list view', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -26,14 +21,13 @@ module('Acceptance | auth-methods list view', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.uid = uuidv4();
|
||||
return authPage.login();
|
||||
return login();
|
||||
});
|
||||
|
||||
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.ok(page.methodsLink.isActive, 'the first link is active');
|
||||
assert.strictEqual(page.methodsLink.text, 'Authentication Methods');
|
||||
assert.dom('[data-test-sidebar-nav-link="Authentication Methods"]').hasClass('active');
|
||||
});
|
||||
|
||||
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 authPath2 = `userpass-2-${this.uid}`;
|
||||
const type = 'userpass';
|
||||
await authEnable.visit();
|
||||
await authEnable.enable(type, authPath1);
|
||||
await authEnable.visit();
|
||||
await authEnable.enable(type, authPath2);
|
||||
await page.visit();
|
||||
// filter by auth type
|
||||
await visit('/vault/settings/auth/enable');
|
||||
await runCmd(mountAuthCmd(type, authPath1));
|
||||
await visit('/vault/settings/auth/enable');
|
||||
await runCmd(mountAuthCmd(type, authPath2));
|
||||
await visit('/vault/access/');
|
||||
|
||||
// filter by auth type
|
||||
await clickTrigger('#filter-by-auth-type');
|
||||
await searchSelect.options.objectAt(0).click();
|
||||
const rows = document.querySelectorAll('[data-test-auth-backend-link]');
|
||||
await click(searchSelect.option(searchSelect.optionIndex(type)));
|
||||
let rows = findAll('[data-test-auth-backend-link]');
|
||||
const rowsUserpass = Array.from(rows).filter((row) => row.innerText.includes('userpass'));
|
||||
|
||||
assert.strictEqual(rows.length, rowsUserpass.length, 'all rows returned are userpass');
|
||||
|
||||
// filter by name
|
||||
await clickTrigger('#filter-by-auth-name');
|
||||
const firstItemToSelect = searchSelect.options.objectAt(0).text;
|
||||
await searchSelect.options.objectAt(0).click();
|
||||
const singleRow = document.querySelectorAll('[data-test-auth-backend-link]');
|
||||
await click(searchSelect.option());
|
||||
const selectedItem = find(`#filter-by-auth-name ${searchSelect.selectedOption()}`).innerText;
|
||||
const singleRow = findAll('[data-test-auth-backend-link]');
|
||||
|
||||
assert.strictEqual(singleRow.length, 1, 'returns only one row');
|
||||
assert.dom(singleRow[0]).includesText(firstItemToSelect, 'shows the filtered by auth name');
|
||||
// clear filter by engine name
|
||||
await searchSelect.deleteButtons.objectAt(1).click();
|
||||
const rowsAgain = document.querySelectorAll('[data-test-auth-backend-link]');
|
||||
assert.ok(rowsAgain.length > 1, 'filter has been removed');
|
||||
assert.dom(singleRow[0]).includesText(selectedItem, 'shows the filtered by auth name');
|
||||
// clear filter by name
|
||||
await click(`#filter-by-auth-name ${searchSelect.removeSelected}`);
|
||||
rows = findAll('[data-test-auth-backend-link]');
|
||||
assert.true(rows.length > 1, 'filter has been removed');
|
||||
|
||||
// cleanup
|
||||
await consoleComponent.runCommands([`delete sys/auth/${authPath1}`]);
|
||||
await consoleComponent.runCommands([`delete sys/auth/${authPath2}`]);
|
||||
await runCmd(`delete sys/auth/${authPath1}`);
|
||||
await runCmd(`delete sys/auth/${authPath2}`);
|
||||
});
|
||||
|
||||
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: {
|
||||
'token/': { accessor: 'auth_token_263b8b4e', type: 'token' },
|
||||
'userpass/': { accessor: 'auth_userpass_87aca1f8', type: 'userpass' },
|
||||
auth: {
|
||||
'token/': { accessor: 'auth_token_263b8b4e', type: 'token' },
|
||||
'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');
|
||||
await authEnable.visit();
|
||||
await visit('/vault/settings/auth/enable');
|
||||
await click('[data-test-sidebar-nav-link="OIDC Provider"]');
|
||||
await page.visit();
|
||||
await visit('/vault/access/');
|
||||
assert
|
||||
.dom('[data-test-auth-backend-link]')
|
||||
.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 click(PKI_KEYS.generateKey);
|
||||
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 click(GENERAL.saveButton);
|
||||
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
|
||||
.dom(PKI_KEYS.nextStepsAlert)
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
* 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 { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
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 { 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) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -25,30 +24,34 @@ module('Acceptance | settings/auth/enable', function (hooks) {
|
||||
// always force the new mount to the top of the list
|
||||
const path = `aaa-approle-${this.uid}`;
|
||||
const type = 'approle';
|
||||
await page.visit();
|
||||
await visit('/vault/settings/auth/enable');
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.auth.enable');
|
||||
await page.enable(type, path);
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
page.flash.latestMessage,
|
||||
`Successfully mounted the ${type} auth method at ${path}.`,
|
||||
'success flash shows'
|
||||
);
|
||||
await click(SES.mountType(type));
|
||||
await fillIn(GENERAL.inputByAttr('path'), path);
|
||||
await click(SES.mountSubmit);
|
||||
assert
|
||||
.dom(GENERAL.latestFlashContent)
|
||||
.hasText(`Successfully mounted the ${type} auth method at ${path}.`);
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.settings.auth.configure.section',
|
||||
'redirects to the auth config page'
|
||||
);
|
||||
|
||||
await listPage.visit();
|
||||
assert.ok(listPage.findLinkById(path), 'mount is present in the list');
|
||||
await visit('/vault/access/');
|
||||
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) {
|
||||
const path = `approle-config-${this.uid}`;
|
||||
const type = 'approle';
|
||||
await page.visit();
|
||||
await page.enable(type, path);
|
||||
await visit('/vault/settings/auth/enable');
|
||||
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
|
||||
// but we still want these forms to continue using sys/auth which returns 0 for default ttl values
|
||||
// check tune form (right after enabling)
|
||||
@@ -64,5 +67,8 @@ module('Acceptance | settings/auth/enable', function (hooks) {
|
||||
await click('[data-test-configure-link]');
|
||||
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');
|
||||
|
||||
// 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 } });
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
this.server.get('sys/internal/ui/mounts', () => {
|
||||
@@ -62,6 +62,6 @@ module('Unit | Adapter | auth method', function (hooks) {
|
||||
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);
|
||||
|
||||
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);
|
||||
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