mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 02:57:59 +00:00
UI/fix kv data cache (#14489)
* KV fetches recent version on every page, no longer disallow new version without metadata access * Don't flash no read permissions warning * Send noMetadataVersion on destroy if version is undefined * test coverage * add changelog, fix tests * Fix failing test
This commit is contained in:
3
changelog/14489.txt
Normal file
3
changelog/14489.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
ui: Fixes caching issue on kv new version create
|
||||||
|
```
|
||||||
@@ -149,6 +149,7 @@ export default ApplicationAdapter.extend({
|
|||||||
} else if (deleteType === 'soft-delete') {
|
} else if (deleteType === 'soft-delete') {
|
||||||
return this.softDelete(backend, path, version);
|
return this.softDelete(backend, path, version);
|
||||||
} else {
|
} else {
|
||||||
|
version = version || currentVersionForNoReadMetadata;
|
||||||
return this.deleteByDeleteType(backend, path, deleteType, version);
|
return this.deleteByDeleteType(backend, path, deleteType, version);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -248,20 +248,7 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
if (modelType === 'secret-v2') {
|
if (modelType === 'secret-v2') {
|
||||||
// after the the base model fetch, kv-v2 has a second associated
|
// after the the base model fetch, kv-v2 has a second associated
|
||||||
// version model that contains the secret data
|
// version model that contains the secret data
|
||||||
|
secretModel = await this.fetchV2Models(capabilities, secretModel, params);
|
||||||
// if no read access to metadata, return current Version from secret data.
|
|
||||||
if (!secretModel.currentVersion) {
|
|
||||||
let adapter = this.store.adapterFor('secret-v2-version');
|
|
||||||
try {
|
|
||||||
secretModel.currentVersion = await adapter.getSecretDataVersion(backend, secret);
|
|
||||||
} catch {
|
|
||||||
// will get error if you have deleted the secret
|
|
||||||
// if this is the case do nothing
|
|
||||||
}
|
|
||||||
secretModel = await this.fetchV2Models(capabilities, secretModel, params);
|
|
||||||
} else {
|
|
||||||
secretModel = await this.fetchV2Models(capabilities, secretModel, params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
secret: secretModel,
|
secret: secretModel,
|
||||||
|
|||||||
@@ -152,13 +152,13 @@
|
|||||||
<form onsubmit={{action "createOrUpdateKey" "edit"}}>
|
<form onsubmit={{action "createOrUpdateKey" "edit"}}>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless padding-top">
|
<div class="box is-sideless is-fullwidth is-marginless padding-top">
|
||||||
<MessageError @model={{@modelForData}} @errorMessage={{this.error}} />
|
<MessageError @model={{@modelForData}} @errorMessage={{this.error}} />
|
||||||
{{#unless @canReadSecretData}}
|
{{#if (eq @canReadSecretData false)}}
|
||||||
<AlertBanner
|
<AlertBanner
|
||||||
@type="warning"
|
@type="warning"
|
||||||
@message="You do not have read permissions. If a secret exists here creating a new secret will overwrite it."
|
@message="You do not have read permissions. If a secret exists here creating a new secret will overwrite it."
|
||||||
data-test-warning-no-read-permissions
|
data-test-warning-no-read-permissions
|
||||||
/>
|
/>
|
||||||
{{/unless}}
|
{{/if}}
|
||||||
<NamespaceReminder @mode="edit" @noun="secret" />
|
<NamespaceReminder @mode="edit" @noun="secret" />
|
||||||
{{#if this.isCreateNewVersionFromOldVersion}}
|
{{#if this.isCreateNewVersionFromOldVersion}}
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
|
|||||||
@@ -92,12 +92,15 @@
|
|||||||
{{#let (concat "vault.cluster.secrets.backend." (if (eq @mode "show") "edit" "show")) as |targetRoute|}}
|
{{#let (concat "vault.cluster.secrets.backend." (if (eq @mode "show") "edit" "show")) as |targetRoute|}}
|
||||||
{{#if @isV2}}
|
{{#if @isV2}}
|
||||||
<ToolbarLink
|
<ToolbarLink
|
||||||
@params={{array targetRoute @model.id (query-params version=@modelForData.version)}}
|
{{! Always create new version from latest if no metadata read access }}
|
||||||
|
@params={{array
|
||||||
|
targetRoute
|
||||||
|
@model.id
|
||||||
|
(query-params version=(if @model.canReadMetadata @modelForData.version ""))
|
||||||
|
}}
|
||||||
@data-test-secret-edit="true"
|
@data-test-secret-edit="true"
|
||||||
@replace={{true}}
|
@replace={{true}}
|
||||||
@type="add"
|
@type="add"
|
||||||
@disabled={{@model.failedServerRead}}
|
|
||||||
@disabledTooltip="Metadata read access is required to create new version"
|
|
||||||
>
|
>
|
||||||
Create new version
|
Create new version
|
||||||
</ToolbarLink>
|
</ToolbarLink>
|
||||||
|
|||||||
@@ -86,14 +86,13 @@ export const testAliasDeleteFromForm = async function (name, itemType, assert) {
|
|||||||
`${itemType}: navigates to edit on create`
|
`${itemType}: navigates to edit on create`
|
||||||
);
|
);
|
||||||
await page.editForm.delete();
|
await page.editForm.delete();
|
||||||
await settled();
|
await page.editForm.waitForConfirm();
|
||||||
await page.editForm.confirmDelete();
|
await page.editForm.confirmDelete();
|
||||||
await settled();
|
await settled();
|
||||||
assert.ok(
|
assert.ok(
|
||||||
aliasIndexPage.flashMessage.latestMessage.startsWith('Successfully deleted'),
|
aliasIndexPage.flashMessage.latestMessage.startsWith('Successfully deleted'),
|
||||||
`${itemType}: shows flash message`
|
`${itemType}: shows flash message`
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
currentRouteName(),
|
currentRouteName(),
|
||||||
'vault.cluster.access.identity.aliases.index',
|
'vault.cluster.access.identity.aliases.index',
|
||||||
|
|||||||
@@ -607,11 +607,22 @@ module('Acceptance | secrets/secret/create', function (hooks) {
|
|||||||
await assert
|
await assert
|
||||||
.dom('[data-test-value-div="secret-key"]')
|
.dom('[data-test-value-div="secret-key"]')
|
||||||
.exists('secret view page and info table row with secret-key value');
|
.exists('secret view page and info table row with secret-key value');
|
||||||
// create new version should be disabled with no metadata read access
|
|
||||||
assert.dom('[data-test-secret-edit]').hasClass('disabled', 'Create new version action is disabled');
|
// Create new version
|
||||||
assert
|
assert.dom('[data-test-secret-edit]').doesNotHaveClass('disabled', 'Create new version is not disabled');
|
||||||
.dom('[data-test-popup-menu-trigger="version"]')
|
await click('[data-test-secret-edit]');
|
||||||
.doesNotExist('the version drop down menu does not show');
|
|
||||||
|
// create new version should not include version in the URL
|
||||||
|
assert.equal(
|
||||||
|
currentURL(),
|
||||||
|
`/vault/secrets/${enginePath}/edit/${secretPath}`,
|
||||||
|
'edit route does not include version query param'
|
||||||
|
);
|
||||||
|
// Update key
|
||||||
|
await editPage.secretKey('newKey');
|
||||||
|
await editPage.secretValue('some-value');
|
||||||
|
await editPage.save();
|
||||||
|
assert.dom('[data-test-value-div="newKey"]').exists('Info row table exists at newKey');
|
||||||
|
|
||||||
// check metadata tab
|
// check metadata tab
|
||||||
await click('[data-test-secret-metadata-tab]');
|
await click('[data-test-secret-metadata-tab]');
|
||||||
@@ -683,8 +694,9 @@ module('Acceptance | secrets/secret/create', function (hooks) {
|
|||||||
await settled(); // eslint-disable-line
|
await settled(); // eslint-disable-line
|
||||||
await click('[data-test-secret-tab]');
|
await click('[data-test-secret-tab]');
|
||||||
await settled(); // eslint-disable-line
|
await settled(); // eslint-disable-line
|
||||||
let text = document.querySelector('[data-test-empty-state-title]').innerText.trim();
|
assert
|
||||||
assert.equal(text, 'Version 1 of this secret has been permanently destroyed');
|
.dom('[data-test-empty-state-title]')
|
||||||
|
.includesText('Version 1 of this secret has been permanently destroyed');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('version 2 with policy with only delete option does not show modal and undelete is an option', async function (assert) {
|
test('version 2 with policy with only delete option does not show modal and undelete is an option', async function (assert) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { clickable, fillable, attribute } from 'ember-cli-page-object';
|
import { clickable, fillable, attribute } from 'ember-cli-page-object';
|
||||||
|
import { waitFor } from '@ember/test-helpers';
|
||||||
import fields from '../form-field';
|
import fields from '../form-field';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -13,4 +14,10 @@ export default {
|
|||||||
submit: clickable('[data-test-identity-submit]'),
|
submit: clickable('[data-test-identity-submit]'),
|
||||||
delete: clickable('[data-test-confirm-action-trigger]'),
|
delete: clickable('[data-test-confirm-action-trigger]'),
|
||||||
confirmDelete: clickable('[data-test-confirm-button]'),
|
confirmDelete: clickable('[data-test-confirm-button]'),
|
||||||
|
waitForConfirm() {
|
||||||
|
return waitFor('[data-test-confirm-button]');
|
||||||
|
},
|
||||||
|
waitForFlash() {
|
||||||
|
return waitFor('[data-test-flash-message-body]');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user