diff --git a/changelog/22471.txt b/changelog/22471.txt new file mode 100644 index 000000000000..67b110cd67d8 --- /dev/null +++ b/changelog/22471.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: enables create and update KV secret workflow when control group present +``` \ No newline at end of file diff --git a/ui/app/components/secret-create-or-update.js b/ui/app/components/secret-create-or-update.js index 8441b2635370..7afc6b832cfe 100644 --- a/ui/app/components/secret-create-or-update.js +++ b/ui/app/components/secret-create-or-update.js @@ -17,6 +17,7 @@ * @isV2=true * @secretData={{@secretData}} * @canCreateSecretMetadata=false + * @buttonDisabled={{this.saving}} * /> * ``` * @param {string} mode - create, edit, show determines what view to display @@ -26,6 +27,7 @@ * @param {boolean} isV2 - whether or not KV1 or KV2 * @param {object} secretData - class that is created in secret-edit * @param {boolean} canUpdateSecretMetadata - based on permissions to the /metadata/ endpoint. If user has secret update. create is not enough for metadata. + * @param {boolean} buttonDisabled - if true, disables the submit button on the create/update form */ import Component from '@glimmer/component'; @@ -51,6 +53,7 @@ export default class SecretCreateOrUpdate extends Component { @tracked validationMessages = null; @service controlGroup; + @service flashMessages; @service router; @service store; @@ -163,6 +166,7 @@ export default class SecretCreateOrUpdate extends Component { if (error instanceof ControlGroupError) { const errorMessage = this.controlGroup.logFromError(error); this.error = errorMessage.content; + this.controlGroup.saveTokenFromError(error); } throw error; }); @@ -233,8 +237,13 @@ export default class SecretCreateOrUpdate extends Component { return; } + const secretPath = type === 'create' ? this.args.modelForData.path : this.args.model.id; this.persistKey(() => { - this.transitionToRoute(SHOW_ROUTE, this.args.model.path || this.args.model.id); + // Show flash message in case there's a control group on read + this.flashMessages.success( + `Secret ${secretPath} ${type === 'create' ? 'created' : 'updated'} successfully.` + ); + this.transitionToRoute(SHOW_ROUTE, secretPath); }); } @action diff --git a/ui/app/services/control-group.js b/ui/app/services/control-group.js index 256dddd0368c..5713e6b27152 100644 --- a/ui/app/services/control-group.js +++ b/ui/app/services/control-group.js @@ -109,6 +109,17 @@ export default Service.extend({ return this.router.transitionTo('vault.cluster.access.control-group-accessor', accessor); }, + // Handle error from non-read request (eg. POST or UPDATE) so it can be retried + saveTokenFromError(error) { + const { accessor, token, creation_path, creation_time, ttl } = error; + const data = { accessor, token, creation_path, creation_time, ttl }; + this.storeControlGroupToken(data); + // In the read flow the accessor is marked once the user clicks "Visit" from the control group page + // On a POST/UPDATE flow we don't redirect, so we need to mark automatically so that on the next try + // the request will attempt unwrap. + this.markTokenForUnwrap(accessor); + }, + logFromError(error) { const { accessor, token, creation_path, creation_time, ttl } = error; const data = { accessor, token, creation_path, creation_time, ttl }; diff --git a/ui/app/templates/components/secret-create-or-update.hbs b/ui/app/templates/components/secret-create-or-update.hbs index baf25602a2eb..7f91804d238a 100644 --- a/ui/app/templates/components/secret-create-or-update.hbs +++ b/ui/app/templates/components/secret-create-or-update.hbs @@ -142,12 +142,7 @@ {{/if}}
-
diff --git a/ui/tests/acceptance/secrets/backend/kv/secret-test.js b/ui/tests/acceptance/secrets/backend/kv/secret-test.js index be1cdd6ea403..22f0259e3ba8 100644 --- a/ui/tests/acceptance/secrets/backend/kv/secret-test.js +++ b/ui/tests/acceptance/secrets/backend/kv/secret-test.js @@ -257,7 +257,7 @@ module('Acceptance | secrets/secret/create, read, delete', function (hooks) { await deleteEngine(enginePath, assert); }); - test('it disables save when validation errors occur', async function (assert) { + test('it shows validation errors', async function (assert) { assert.expect(5); const enginePath = `kv-secret-${this.uid}`; const secretPath = 'not-duplicate'; @@ -279,7 +279,7 @@ module('Acceptance | secrets/secret/create, read, delete', function (hooks) { assert .dom('[data-test-input="maxVersions"]') .hasClass('has-error-border', 'shows border error on input with error'); - assert.dom('[data-test-secret-save]').isDisabled('Save button is disabled'); + assert.dom('[data-test-secret-save]').isNotDisabled('Save button is disabled'); await fillIn('[data-test-input="maxVersions"]', 20); // fillIn replaces the text, whereas typeIn only adds to it. await triggerKeyEvent('[data-test-input="maxVersions"]', 'keyup', 65); await editPage.path(secretPath);