From 225cd9d19fe9b0b85c279d7bcfe8e589c44f6da5 Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:19:22 +0100 Subject: [PATCH] UI: Align auth method ttl with tune value (#26663) * refactor findAll to use internal/ui/mounts when authenticated as well * format ttl in details view * include hours in format for easy comparison to CLI return * Revert "include hours in format for easy comparison to CLI return" This reverts commit 990aaf5d1e157ccd83389ecd54011b8971f7e52d. * add changelog * revert adapter change * add new adapter method instead of updating existing * add test for ttl * revert and use findAll again * update mirage endpoints * remove query obj * Revert "update mirage endpoints" This reverts commit f5fb333bf46b8ee86fbd134cbbd9fde85a72c9a1. * another one that snuck into a separate commit * use adapterOption to manage endpoint logic * add adapter tests * Update changelog/26663.txt Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> * add test that ttl inputs aren not checked --------- Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> --- changelog/26663.txt | 3 + ui/app/adapters/auth-method.js | 18 +++-- ui/app/routes/vault/cluster/access/method.js | 40 +++++------ .../components/auth-method/configuration.hbs | 2 + .../vault/cluster/settings/auth/configure.hbs | 2 +- .../acceptance/settings/auth/enable-test.js | 25 ++++++- ui/tests/helpers/general-selectors.ts | 7 +- ui/tests/unit/adapters/auth-method-test.js | 67 +++++++++++++++++++ 8 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 changelog/26663.txt create mode 100644 ui/tests/unit/adapters/auth-method-test.js diff --git a/changelog/26663.txt b/changelog/26663.txt new file mode 100644 index 0000000000..ac29ce0ece --- /dev/null +++ b/changelog/26663.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Show computed values from `sys/internal/ui/mounts` endpoint for auth mount configuration view +``` diff --git a/ui/app/adapters/auth-method.js b/ui/app/adapters/auth-method.js index 449383911b..f23146de3a 100644 --- a/ui/app/adapters/auth-method.js +++ b/ui/app/adapters/auth-method.js @@ -21,20 +21,26 @@ export default ApplicationAdapter.extend({ findAll(store, type, sinceToken, snapshotRecordArray) { const isUnauthenticated = snapshotRecordArray?.adapterOptions?.unauthenticated; - if (isUnauthenticated) { + // 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) { const url = `/${this.urlPrefix()}/internal/ui/mounts`; return this.ajax(url, 'GET', { - unauthenticated: true, + unauthenticated: isUnauthenticated, }) .then((result) => { return { data: result.data.auth, }; }) - .catch(() => { - return { - data: {}, - }; + .catch((e) => { + if (isUnauthenticated) return { data: {} }; + + if (e instanceof AdapterError) { + set(e, 'policyPath', 'sys/internal/ui/mounts'); + } + throw e; }); } return this.ajax(this.url(), 'GET').catch((e) => { diff --git a/ui/app/routes/vault/cluster/access/method.js b/ui/app/routes/vault/cluster/access/method.js index 5ed006bb45..1eeb581bdf 100644 --- a/ui/app/routes/vault/cluster/access/method.js +++ b/ui/app/routes/vault/cluster/access/method.js @@ -15,26 +15,28 @@ export default Route.extend({ model(params) { const { path } = params; - return this.store.findAll('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 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 model; - } - return this.pathHelp.getPaths(model.apiPath, path).then((paths) => { - model.set('paths', paths); - return model; }); - }); }, }); diff --git a/ui/app/templates/components/auth-method/configuration.hbs b/ui/app/templates/components/auth-method/configuration.hbs index bddbfdc139..9c5b85e060 100644 --- a/ui/app/templates/components/auth-method/configuration.hbs +++ b/ui/app/templates/components/auth-method/configuration.hbs @@ -10,12 +10,14 @@ @alwaysRender={{not (is-empty-value (get @model attr.name))}} @label={{or attr.options.label (to-label attr.name)}} @value={{stringify (get @model attr.name)}} + @formatTtl={{eq attr.options.editType "ttl"}} /> {{else}} {{/if}} {{/each}} diff --git a/ui/app/templates/vault/cluster/settings/auth/configure.hbs b/ui/app/templates/vault/cluster/settings/auth/configure.hbs index d75976c85d..bf71eb34c5 100644 --- a/ui/app/templates/vault/cluster/settings/auth/configure.hbs +++ b/ui/app/templates/vault/cluster/settings/auth/configure.hbs @@ -5,7 +5,7 @@ - + diff --git a/ui/tests/acceptance/settings/auth/enable-test.js b/ui/tests/acceptance/settings/auth/enable-test.js index 45a04123e7..0c40bab40a 100644 --- a/ui/tests/acceptance/settings/auth/enable-test.js +++ b/ui/tests/acceptance/settings/auth/enable-test.js @@ -3,11 +3,12 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import { currentRouteName, settled } from '@ember/test-helpers'; +import { click, currentRouteName, settled } 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 authPage from 'vault/tests/pages/auth'; @@ -42,4 +43,26 @@ module('Acceptance | settings/auth/enable', function (hooks) { await listPage.visit(); assert.ok(listPage.findLinkById(path), 'mount is present in the list'); }); + + 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); + // 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) + assert.dom(GENERAL.toggleInput('Default Lease TTL')).isNotChecked('default lease ttl is unset'); + assert.dom(GENERAL.toggleInput('Max Lease TTL')).isNotChecked('max lease ttl is unset'); + await click(GENERAL.breadcrumbAtIdx(1)); + assert + .dom(GENERAL.infoRowValue('Default Lease TTL')) + .hasText('1 month 1 day', 'shows system default TTL'); + assert.dom(GENERAL.infoRowValue('Max Lease TTL')).hasText('1 month 1 day', 'shows the proper max TTL'); + + // check edit form TTL values + 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'); + }); }); diff --git a/ui/tests/helpers/general-selectors.ts b/ui/tests/helpers/general-selectors.ts index 1617ff59be..a4cc9bbe51 100644 --- a/ui/tests/helpers/general-selectors.ts +++ b/ui/tests/helpers/general-selectors.ts @@ -29,13 +29,14 @@ export const GENERAL = { menuTrigger: '[data-test-popup-menu-trigger]', listItem: '[data-test-list-item-link]', // FORMS + checkboxByAttr: (attr: string) => `[data-test-checkbox="${attr}"]`, + enableField: (attr: string) => `[data-test-enable-field="${attr}"] button`, + fieldByAttr: (attr: string) => `[data-test-field="${attr}"]`, infoRowLabel: (label: string) => `[data-test-row-label="${label}"]`, infoRowValue: (label: string) => `[data-test-value-div="${label}"]`, inputByAttr: (attr: string) => `[data-test-input="${attr}"]`, selectByAttr: (attr: string) => `[data-test-select="${attr}"]`, - checkboxByAttr: (attr: string) => `[data-test-checkbox="${attr}"]`, - fieldByAttr: (attr: string) => `[data-test-field="${attr}"]`, - enableField: (attr: string) => `[data-test-enable-field="${attr}"] button`, + toggleInput: (attr: string) => `[data-test-toggle-input="${attr}"]`, ttl: { toggle: (attr: string) => `[data-test-toggle-label="${attr}"]`, input: (attr: string) => `[data-test-ttl-value="${attr}"]`, diff --git a/ui/tests/unit/adapters/auth-method-test.js b/ui/tests/unit/adapters/auth-method-test.js new file mode 100644 index 0000000000..5210026114 --- /dev/null +++ b/ui/tests/unit/adapters/auth-method-test.js @@ -0,0 +1,67 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; + +module('Unit | Adapter | auth method', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(async function () { + this.store = this.owner.lookup('service:store'); + this.mockResponse = { + data: { + auth: { + 'approle/': { + accessor: 'auth_approle_43e5a627', + config: { + default_lease_ttl: 2764800, + force_no_cache: false, + listing_visibility: 'hidden', + max_lease_ttl: 2764800, + token_type: 'default-service', + }, + uuid: '7a8bc146-76d0-3a9c-9feb-47a6713a85b3', + }, + }, + }, + }; + }); + + test('findAll makes request to correct endpoint with no adapterOptions', async function (assert) { + assert.expect(1); + + this.server.get('sys/auth', () => { + assert.ok(true, 'request made to sys/auth when no options are passed to findAll'); + return { data: this.mockResponse.data.auth }; + }); + + await this.store.findAll('auth-method'); + }); + + test('findAll makes request to correct endpoint when unauthenticated is true', async function (assert) { + assert.expect(1); + + this.server.get('sys/internal/ui/mounts', () => { + assert.ok(true, 'request made to correct endpoint when unauthenticated'); + return this.mockResponse; + }); + + await this.store.findAll('auth-method', { adapterOptions: { unauthenticated: true } }); + }); + + test('findAll makes request to correct endpoint when useMountsEndpoint is true', async function (assert) { + assert.expect(1); + + this.server.get('sys/internal/ui/mounts', () => { + assert.ok(true, 'request made to correct endpoint when useMountsEndpoint'); + return this.mockResponse; + }); + + await this.store.findAll('auth-method', { adapterOptions: { useMountsEndpoint: true } }); + }); +});