From 05c1fb0771fdbddb855096bbdd0cb5827cef8728 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Fri, 7 Apr 2023 16:05:29 -0500 Subject: [PATCH] UI: Mount PKI options + allowed_managed_keys (#19791) --- changelog/19791.txt | 3 ++ ui/app/components/mount-backend-form.js | 12 +++++++ .../decorators/model-expanded-attributes.js | 9 ++++- ui/app/helpers/mountable-secret-engines.js | 1 + ui/app/models/mount-config.js | 5 ++- ui/app/models/secret-engine.js | 17 +++++++-- .../cluster/secrets/backend/configuration.hbs | 5 +-- .../settings/mount-secret-backend-test.js | 36 +++++++++++++++---- .../pages/secrets/backend/configuration.js | 4 +-- ui/tests/unit/models/secret-engine-test.js | 32 +++++++++++++++++ 10 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 changelog/19791.txt diff --git a/changelog/19791.txt b/changelog/19791.txt new file mode 100644 index 000000000000..26722cde3133 --- /dev/null +++ b/changelog/19791.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: add allowed_managed_keys field to secret engine mount options +``` diff --git a/ui/app/components/mount-backend-form.js b/ui/app/components/mount-backend-form.js index 6fa1412654ba..6b09f5e1f02f 100644 --- a/ui/app/components/mount-backend-form.js +++ b/ui/app/components/mount-backend-form.js @@ -56,6 +56,17 @@ export default class MountBackendForm extends Component { } } + typeChangeSideEffect(type) { + if (!this.args.mountType === 'secret') return; + if (type === 'pki') { + // If type PKI, set max lease to ~10years + this.args.mountModel.config.maxLeaseTtl = '3650d'; + } else { + // otherwise reset + this.args.mountModel.config.maxLeaseTtl = 0; + } + } + checkModelValidity(model) { const { isValid, state, invalidFormMessage } = model.validate(); this.modelValidations = state; @@ -158,6 +169,7 @@ export default class MountBackendForm extends Component { @action setMountType(value) { this.args.mountModel.type = value; + this.typeChangeSideEffect(value); this.checkPathChange(value); } } diff --git a/ui/app/decorators/model-expanded-attributes.js b/ui/app/decorators/model-expanded-attributes.js index 60db028a30ad..a5aa840ba041 100644 --- a/ui/app/decorators/model-expanded-attributes.js +++ b/ui/app/decorators/model-expanded-attributes.js @@ -73,7 +73,14 @@ export function withExpandedAttributes() { }); const expanded = expandAttributeMeta(rModel, rAttrNames); expanded.forEach((attr) => { - byKey[`${name}.${attr.name}`] = attr; + byKey[`${name}.${attr.name}`] = { + ...attr, + options: { + ...attr.options, + // This ensures the correct path is updated in FormField + fieldValue: `${name}.${attr.fieldValue || attr.name}`, + }, + }; }); }, this); this._allByKey = byKey; diff --git a/ui/app/helpers/mountable-secret-engines.js b/ui/app/helpers/mountable-secret-engines.js index a771f4bd993f..33f1228fe4e8 100644 --- a/ui/app/helpers/mountable-secret-engines.js +++ b/ui/app/helpers/mountable-secret-engines.js @@ -87,6 +87,7 @@ const MOUNTABLE_SECRET_ENGINES = [ { displayName: 'PKI Certificates', type: 'pki', + // engineRoute: 'pki.overview', // TODO VAULT-13822 category: 'generic', }, { diff --git a/ui/app/models/mount-config.js b/ui/app/models/mount-config.js index 54dd1f678919..8f938b259907 100644 --- a/ui/app/models/mount-config.js +++ b/ui/app/models/mount-config.js @@ -63,5 +63,8 @@ export default class MountConfigModel extends Model { }) tokenType; - @attr() allowedManagedKeys; + @attr({ + editType: 'stringArray', + }) + allowedManagedKeys; } diff --git a/ui/app/models/secret-engine.js b/ui/app/models/secret-engine.js index 9734299088a7..dbe083c4e3eb 100644 --- a/ui/app/models/secret-engine.js +++ b/ui/app/models/secret-engine.js @@ -155,6 +155,7 @@ export default class SecretEngineModel extends Model { fields.push('config.defaultLeaseTtl', 'config.maxLeaseTtl'); } fields.push( + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -189,6 +190,7 @@ export default class SecretEngineModel extends Model { ...CORE_OPTIONS, 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', ...STANDARD_CONFIG, ]; break; @@ -198,21 +200,32 @@ export default class SecretEngineModel extends Model { ...CORE_OPTIONS, 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', ...STANDARD_CONFIG, ]; break; case 'database': // Highlight TTLs in default defaultFields = ['path', 'config.defaultLeaseTtl', 'config.maxLeaseTtl']; + optionFields = [...CORE_OPTIONS, 'config.allowedManagedKeys', ...STANDARD_CONFIG]; + break; + case 'pki': + defaultFields = ['path', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', 'config.allowedManagedKeys']; optionFields = [...CORE_OPTIONS, ...STANDARD_CONFIG]; break; case 'keymgmt': // no ttl options for keymgmt - optionFields = [...CORE_OPTIONS, ...STANDARD_CONFIG]; + optionFields = [...CORE_OPTIONS, 'config.allowedManagedKeys', ...STANDARD_CONFIG]; break; default: defaultFields = ['path']; - optionFields = [...CORE_OPTIONS, 'config.defaultLeaseTtl', 'config.maxLeaseTtl', ...STANDARD_CONFIG]; + optionFields = [ + ...CORE_OPTIONS, + 'config.defaultLeaseTtl', + 'config.maxLeaseTtl', + 'config.allowedManagedKeys', + ...STANDARD_CONFIG, + ]; break; } diff --git a/ui/app/templates/vault/cluster/secrets/backend/configuration.hbs b/ui/app/templates/vault/cluster/secrets/backend/configuration.hbs index ba60af34649c..7890b5d31667 100644 --- a/ui/app/templates/vault/cluster/secrets/backend/configuration.hbs +++ b/ui/app/templates/vault/cluster/secrets/backend/configuration.hbs @@ -29,13 +29,14 @@ {{else}} {{/if}} {{/each}} diff --git a/ui/tests/acceptance/settings/mount-secret-backend-test.js b/ui/tests/acceptance/settings/mount-secret-backend-test.js index a95509536657..33c9152e5994 100644 --- a/ui/tests/acceptance/settings/mount-secret-backend-test.js +++ b/ui/tests/acceptance/settings/mount-secret-backend-test.js @@ -32,8 +32,6 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) { const path = `mount-kv-${this.uid}`; const defaultTTLHours = 100; const maxTTLHours = 300; - const defaultTTLSeconds = (defaultTTLHours * 60 * 60).toString(); - const maxTTLSeconds = (maxTTLHours * 60 * 60).toString(); await page.visit(); @@ -51,15 +49,14 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) { .maxTTLVal(maxTTLHours) .submit(); await configPage.visit({ backend: path }); - assert.strictEqual(configPage.defaultTTL, `${defaultTTLSeconds}s`, 'shows the proper TTL'); - assert.strictEqual(configPage.maxTTL, `${maxTTLSeconds}s`, 'shows the proper max TTL'); + assert.strictEqual(configPage.defaultTTL, `${defaultTTLHours}h`, 'shows the proper TTL'); + assert.strictEqual(configPage.maxTTL, `${maxTTLHours}h`, 'shows the proper max TTL'); }); test('it sets the ttl when enabled then disabled', async function (assert) { // always force the new mount to the top of the list const path = `mount-kv-${this.uid}`; const maxTTLHours = 300; - const maxTTLSeconds = (maxTTLHours * 60 * 60).toString(); await page.visit(); @@ -76,8 +73,33 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) { .maxTTLVal(maxTTLHours) .submit(); await configPage.visit({ backend: path }); - assert.strictEqual(configPage.defaultTTL, '0', 'shows the proper TTL'); - assert.strictEqual(configPage.maxTTL, `${maxTTLSeconds}s`, 'shows the proper max TTL'); + assert.strictEqual(configPage.defaultTTL, '0s', 'shows the proper TTL'); + assert.strictEqual(configPage.maxTTL, `${maxTTLHours}h`, 'shows the proper max TTL'); + }); + + test('it sets the max ttl after pki chosen, resets after', async function (assert) { + await page.visit(); + assert.strictEqual(currentRouteName(), 'vault.cluster.settings.mount-secret-backend'); + await page.selectType('pki'); + await page.next(); + assert.dom('[data-test-input="maxLeaseTtl"]').exists(); + assert + .dom('[data-test-input="maxLeaseTtl"] [data-test-ttl-toggle]') + .isChecked('Toggle is checked by default'); + assert.dom('[data-test-input="maxLeaseTtl"] [data-test-ttl-value]').hasValue('3650'); + assert.dom('[data-test-input="maxLeaseTtl"] [data-test-select="ttl-unit"]').hasValue('d'); + + // Go back and choose a different type + await page.back(); + await page.selectType('database'); + await page.next(); + assert.dom('[data-test-input="maxLeaseTtl"]').exists('3650'); + assert + .dom('[data-test-input="maxLeaseTtl"] [data-test-ttl-toggle]') + .isNotChecked('Toggle is unchecked by default'); + await page.enableMaxTtl(); + assert.dom('[data-test-input="maxLeaseTtl"] [data-test-ttl-value]').hasValue(''); + assert.dom('[data-test-input="maxLeaseTtl"] [data-test-select="ttl-unit"]').hasValue('s'); }); test('it throws error if setting duplicate path name', async function (assert) { diff --git a/ui/tests/pages/secrets/backend/configuration.js b/ui/tests/pages/secrets/backend/configuration.js index 00251269958b..2ea641a0d8b9 100644 --- a/ui/tests/pages/secrets/backend/configuration.js +++ b/ui/tests/pages/secrets/backend/configuration.js @@ -7,6 +7,6 @@ import { create, visitable, text } from 'ember-cli-page-object'; export default create({ visit: visitable('/vault/secrets/:backend/configuration'), - defaultTTL: text('[data-test-row-value="Default Lease TTL"]'), - maxTTL: text('[data-test-row-value="Max Lease TTL"]'), + defaultTTL: text('[data-test-value-div="Default Lease TTL"]'), + maxTTL: text('[data-test-value-div="Max Lease TTL"]'), }); diff --git a/ui/tests/unit/models/secret-engine-test.js b/ui/tests/unit/models/secret-engine-test.js index 1b977dbb68d7..c82a77efbf3c 100644 --- a/ui/tests/unit/models/secret-engine-test.js +++ b/ui/tests/unit/models/secret-engine-test.js @@ -66,6 +66,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'sealWrap', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -88,6 +89,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'sealWrap', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -112,6 +114,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'sealWrap', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -136,6 +139,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'accessor', 'local', 'sealWrap', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -161,6 +165,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'sealWrap', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -186,6 +191,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'sealWrap', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -212,6 +218,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'sealWrap', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders', @@ -229,6 +236,30 @@ module('Unit | Model | secret-engine', function (hooks) { assert.deepEqual(model.get('formFieldGroups'), [ { default: ['path', 'config.defaultLeaseTtl', 'config.maxLeaseTtl'] }, + { + 'Method Options': [ + 'description', + 'config.listingVisibility', + 'local', + 'sealWrap', + 'config.allowedManagedKeys', + 'config.auditNonHmacRequestKeys', + 'config.auditNonHmacResponseKeys', + 'config.passthroughRequestHeaders', + 'config.allowedResponseHeaders', + ], + }, + ]); + }); + + test('returns correct values for pki', function (assert) { + assert.expect(1); + const model = this.store.createRecord('secret-engine', { + type: 'pki', + }); + + assert.deepEqual(model.get('formFieldGroups'), [ + { default: ['path', 'config.defaultLeaseTtl', 'config.maxLeaseTtl', 'config.allowedManagedKeys'] }, { 'Method Options': [ 'description', @@ -258,6 +289,7 @@ module('Unit | Model | secret-engine', function (hooks) { 'config.listingVisibility', 'local', 'sealWrap', + 'config.allowedManagedKeys', 'config.auditNonHmacRequestKeys', 'config.auditNonHmacResponseKeys', 'config.passthroughRequestHeaders',