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 } });
+ });
+});