mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-03 20:40:45 -05:00
UI: Fix Showing KMIP Credentials (#30778)
* move role-form files
* Revert "move role-form files"
This reverts commit ad16dd059b.
* show credentials after generating
* add changelog
* periods
This commit is contained in:
parent
71071183a6
commit
1b61e2e187
8 changed files with 144 additions and 62 deletions
3
changelog/30778.txt
Normal file
3
changelog/30778.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
ui/kmip: Fixes KMIP credentials view and displays `private_key` after generating
|
||||
```
|
||||
62
ui/lib/kmip/addon/components/details-credentials.hbs
Normal file
62
ui/lib/kmip/addon/components/details-credentials.hbs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if @credentials.deletePath.canDelete}}
|
||||
<ConfirmAction
|
||||
@buttonText="Revoke credentials"
|
||||
class="toolbar-button"
|
||||
@buttonColor="secondary"
|
||||
@onConfirmAction={{@onRevokeCredentials}}
|
||||
@confirmTitle="Revoke this?"
|
||||
@confirmMessage="Any client using these credentials will no longer be able to."
|
||||
/>
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
<Hds::Copy::Button
|
||||
@text="Copy certificate"
|
||||
@textToCopy={{@credentials.certificate}}
|
||||
@onError={{(fn (set-flash-message "Clipboard copy failed. The Clipboard API requires a secure context." "danger"))}}
|
||||
class="toolbar-button"
|
||||
data-test-copy-button
|
||||
/>
|
||||
<div class="toolbar-separator"></div>
|
||||
<ToolbarLink
|
||||
@route="credentials.index"
|
||||
@models={{array @credentials.scope @credentials.role}}
|
||||
data-test-kmip-link-back-to-role
|
||||
>
|
||||
Back to role
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<InfoTableRow @label="Serial number" @value={{@credentials.id}}>
|
||||
<MaskedInput @value={{@credentials.id}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{#if @credentials.privateKey}}
|
||||
<InfoTableRow @label="Private key" @value={{@credentials.privateKey}}>
|
||||
<div class="is-block">
|
||||
<Hds::Alert data-test-warning @type="inline" @color="warning" @class="has-bottom-margin-s" as |A|>
|
||||
<A.Title>Warning</A.Title>
|
||||
<A.Description>You will not be able to access the private key later, so please copy the information below.</A.Description>
|
||||
</Hds::Alert>
|
||||
<MaskedInput @value={{@credentials.privateKey}} @name="Private key" @allowCopy={{true}} @displayOnly={{true}} />
|
||||
</div>
|
||||
</InfoTableRow>
|
||||
{{/if}}
|
||||
<InfoTableRow @label="Certificate" @value={{@credentials.certificate}}>
|
||||
<MaskedInput @value={{@credentials.certificate}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
<InfoTableRow @label="CA Chain" @value={{@credentials.caChain}}>
|
||||
<div class="is-block">
|
||||
{{#each @credentials.caChain as |chain|}}
|
||||
<MaskedInput @value={{chain}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
</InfoTableRow>
|
||||
</div>
|
||||
16
ui/lib/kmip/addon/components/page/credentials-generate.hbs
Normal file
16
ui/lib/kmip/addon/components/page/credentials-generate.hbs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
{{#if this.hasGenerated}}
|
||||
<DetailsCredentials @credentials={{@credentials}} @onRevokeCredentials={{this.revokeCredentials}} />
|
||||
{{else}}
|
||||
<EditForm
|
||||
@model={{@credentials}}
|
||||
@onSave={{fn (mut this.hasGenerated) true}}
|
||||
@callOnSaveAfterRender={{true}}
|
||||
@successMessage="Successfully generated credentials from role: {{@credentials.role}}!"
|
||||
@cancelLinkParams={{array "credentials.index"}}
|
||||
/>
|
||||
{{/if}}
|
||||
30
ui/lib/kmip/addon/components/page/credentials-generate.js
Normal file
30
ui/lib/kmip/addon/components/page/credentials-generate.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import errorMessage from 'vault/utils/error-message';
|
||||
|
||||
export default class KmipPageCredentialsGenerate extends Component {
|
||||
@service flashMessages;
|
||||
@service('app-router') router;
|
||||
@tracked hasGenerated = false;
|
||||
|
||||
@action
|
||||
async revokeCredentials() {
|
||||
const { scope, role } = this.args.credentials;
|
||||
try {
|
||||
await this.args.credentials.destroyRecord();
|
||||
this.flashMessages.success('Successfully revoked credentials.');
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.kmip.credentials.index', scope, role);
|
||||
} catch (e) {
|
||||
const message = errorMessage(e);
|
||||
this.flashMessages.danger(`There was an error revoking credentials: ${message}.`);
|
||||
this.args.credentials.rollbackAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,10 @@ export default class CredentialsShowController extends Controller {
|
|||
async revokeCredentials() {
|
||||
try {
|
||||
await this.model.destroyRecord();
|
||||
this.flashMessages.success('Successfully revoked credentials');
|
||||
this.flashMessages.success('Successfully revoked credentials.');
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.kmip.credentials.index', this.scope, this.role);
|
||||
} catch (e) {
|
||||
this.flashMessages.danger(`There was an error revoking credentials: ${e.errors.join(' ')}`);
|
||||
this.flashMessages.danger(`There was an error revoking credentials: ${e.errors.join(' ')}.`);
|
||||
this.model.rollbackAttributes();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@
|
|||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
<EditForm
|
||||
@model={{this.model}}
|
||||
@onSave={{transition-to "vault.cluster.secrets.backend.kmip.credentials.show" this.model.id}}
|
||||
@callOnSaveAfterRender={{true}}
|
||||
@successMessage={{concat "Successfully generated credentials from role:" this.role "!"}}
|
||||
@cancelLinkParams={{array "credentials.index"}}
|
||||
/>
|
||||
|
||||
{{! this.model is the ember data model kmip/credential }}
|
||||
<Page::CredentialsGenerate @credentials={{this.model}} />
|
||||
|
|
@ -13,54 +13,5 @@
|
|||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if this.model.deletePath.canDelete}}
|
||||
<ConfirmAction
|
||||
@buttonText="Revoke credentials"
|
||||
class="toolbar-button"
|
||||
@buttonColor="secondary"
|
||||
@onConfirmAction={{this.revokeCredentials}}
|
||||
@confirmTitle="Revoke this?"
|
||||
@confirmMessage="Any client using these credentials will no longer be able to."
|
||||
/>
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
<ToolbarLink @route="credentials.index" @models={{array this.scope this.role}} data-test-kmip-link-back-to-role>
|
||||
Back to role
|
||||
</ToolbarLink>
|
||||
<Hds::Copy::Button
|
||||
@text="Copy certificate"
|
||||
@textToCopy={{this.model.certificate}}
|
||||
@onError={{(fn (set-flash-message "Clipboard copy failed. The Clipboard API requires a secure context." "danger"))}}
|
||||
class="toolbar-button"
|
||||
data-test-copy-button
|
||||
/>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<InfoTableRow @label="Serial number" @value={{this.model.id}}>
|
||||
<MaskedInput @value={{this.model.id}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{#if this.model.privateKey}}
|
||||
<InfoTableRow @label="Private key" @value={{this.model.privateKey}}>
|
||||
<div class="is-block">
|
||||
<Hds::Alert data-test-warning @type="inline" @color="warning" @class="has-bottom-margin-s" as |A|>
|
||||
<A.Title>Warning</A.Title>
|
||||
<A.Description>You will not be able to access the private key later, so please copy the information below.</A.Description>
|
||||
</Hds::Alert>
|
||||
<MaskedInput @value={{this.model.privateKey}} @name="Private key" @allowCopy={{true}} @displayOnly={{true}} />
|
||||
</div>
|
||||
</InfoTableRow>
|
||||
{{/if}}
|
||||
<InfoTableRow @label="Certificate" @value={{this.model.certificate}}>
|
||||
<MaskedInput @value={{this.model.certificate}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
<InfoTableRow @label="CA Chain" @value={{this.model.caChain}}>
|
||||
<div class="is-block">
|
||||
{{#each this.model.caChain as |chain|}}
|
||||
<MaskedInput @value={{chain}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
</InfoTableRow>
|
||||
</div>
|
||||
|
||||
<DetailsCredentials @credentials={{this.model}} @onRevokeCredentials={{this.revokeCredentials}} />
|
||||
|
|
@ -335,14 +335,38 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) {
|
|||
await settled();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.kmip.credentials.show',
|
||||
'generate redirects to the show page'
|
||||
'vault.cluster.secrets.backend.kmip.credentials.generate',
|
||||
'it remains in the generate route'
|
||||
);
|
||||
assert
|
||||
.dom(GENERAL.infoRowValue('Private key'))
|
||||
.hasText(
|
||||
'Warning You will not be able to access the private key later, so please copy the information below. ***********',
|
||||
'it renders private key after generating'
|
||||
);
|
||||
await credentialsPage.backToRoleLink();
|
||||
await settled();
|
||||
assert.strictEqual(credentialsPage.listItemLinks.length, 1, 'renders a single credential');
|
||||
});
|
||||
|
||||
test('it can revoke a credential from the generate view', async function (assert) {
|
||||
const { backend, scope, role } = await createRole(this.backend);
|
||||
await credentialsPage.visit({ backend, scope, role });
|
||||
await credentialsPage.generateCredentialsLink();
|
||||
await credentialsPage.submit();
|
||||
// revoke the credentials
|
||||
await waitUntil(() => find('[data-test-confirm-action-trigger]'));
|
||||
assert.dom('[data-test-confirm-action-trigger]').exists('delete button exists');
|
||||
await credentialsPage.delete().confirmDelete();
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
`/vault/secrets/${backend}/kmip/scopes/${scope}/roles/${role}/credentials`,
|
||||
'redirects to the credentials list'
|
||||
);
|
||||
assert.true(credentialsPage.isEmpty, 'renders an empty credentials page');
|
||||
});
|
||||
|
||||
test('it can revoke a credential from the list', async function (assert) {
|
||||
const { backend, scope, role } = await generateCreds(this.backend);
|
||||
await credentialsPage.visit({ backend, scope, role });
|
||||
|
|
|
|||
Loading…
Reference in a new issue