KV custom metadata test coverage (#12464)

* test coverage

* small changes

* another small change

* fix test

* browserstack blah

* add page object
This commit is contained in:
Angel Garbarino 2021-09-03 11:08:26 -06:00 committed by GitHub
parent 0704d5b2de
commit 88125d41ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 21 deletions

View file

@ -136,8 +136,16 @@
{{#if (eq @mode "edit")}}
<form onsubmit={{action "createOrUpdateKey" "edit"}}>
<div class="box is-sideless is-fullwidth is-marginless is-paddingless">
<div class="box is-sideless is-fullwidth is-marginless padding-top">
{{#if @model.canReadSecretData}}
<MessageError @model={{@modelForData}} @errorMessage={{this.error}} />
{{else}}
<AlertBanner
@type="warning"
@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
/>
{{/if}}
<NamespaceReminder @mode="edit" @noun="secret" />
{{#if this.isCreateNewVersionFromOldVersion}}
<div class="form-section">

View file

@ -37,6 +37,7 @@
type="submit"
disabled={{this.validationErrorCount}}
class="button is-primary"
data-test-save-metadata
>
Save
</button>

View file

@ -55,7 +55,11 @@
@title="No custom metadata"
@bottomBorder={{true}}
@message="This data is version-agnostic and is usually used to describe the secret being stored.">
<LinkTo @route="vault.cluster.secrets.backend.edit-metadata" @model={{this.model.id}}>
<LinkTo
@route="vault.cluster.secrets.backend.edit-metadata"
@model={{this.model.id}}
data-test-add-custom-metadata
>
Add metadata
</LinkTo>
</EmptyState>

View file

@ -11,7 +11,7 @@
<Icon @size="s" @glyph="minus-plain"/>
{{/if}}
</div>
<div class="column is-flex" data-test-value-div>
<div class="column is-flex" data-test-value-div="{{label}}">
{{#if (has-block)}}
{{yield}}
{{else if valueIsBoolean}}
@ -21,6 +21,7 @@
class="icon-true"
@size="l"
@glyph="check-circle-outline"
data-test-boolean-true
/> Yes
{{else}}
<Icon

View file

@ -6,6 +6,7 @@ import {
currentRouteName,
fillIn,
triggerKeyEvent,
typeIn,
} from '@ember/test-helpers';
import { create } from 'ember-cli-page-object';
import { module, test } from 'qunit';
@ -69,15 +70,113 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.ok(showPage.editIsPresent, 'shows the edit button');
});
test('it can create a secret with a non default max version', async function(assert) {
test('it can create a secret with a non default max version and add metadata', async function(assert) {
let enginePath = `kv-${new Date().getTime()}`;
let secretPath = 'maxVersions';
let maxVersions = 101;
await mountSecrets.visit();
await mountSecrets.enable('kv', enginePath);
await settled();
await click('[data-test-secret-create="true"]');
await fillIn('[data-test-secret-path="true"]', secretPath);
await editPage.startCreateSecret();
await editPage.path(secretPath);
await editPage.toggleMetadata();
await settled();
await editPage.maxVersion(maxVersions);
await settled();
await editPage.save();
await settled();
await editPage.metadataTab();
await settled();
let savedMaxVersions = Number(
document.querySelector('[data-test-value-div="Maximum versions"]').innerText
);
assert.equal(
maxVersions,
savedMaxVersions,
'max_version displays the saved number set when creating the secret'
);
// add metadata
await click('[data-test-add-custom-metadata]');
await fillIn('[data-test-kv-key]', 'key');
await fillIn('[data-test-kv-value]', 'value');
await click('[data-test-save-metadata]');
let key = document.querySelector('[data-test-row-label="key"]').innerText;
let value = document.querySelector('[data-test-row-value="key"]').innerText;
assert.equal(key, 'key', 'metadata key displays after adding it.');
assert.equal(value, 'value', 'metadata value displays after adding it.');
});
test('it can handle validation on custom metadata', async function(assert) {
let enginePath = `kv-${new Date().getTime()}`;
let secretPath = 'customMetadataValidations';
await mountSecrets.visit();
await mountSecrets.enable('kv', enginePath);
await settled();
await editPage.startCreateSecret();
await editPage.path(secretPath);
await editPage.toggleMetadata();
await settled();
await typeIn('[data-test-kv-value]', 'invalid\\/');
assert
.dom('[data-test-inline-error-message]')
.hasText('Custom values cannot contain a backward slash.', 'will not allow backward slash in value.');
//remove validation error and cause another error that is captured by the API
await fillIn('[data-test-kv-value]', 'removed');
await typeIn('[data-test-kv-value]', '!');
await click('[data-test-secret-save="true"]');
assert
.dom('[data-test-error]')
.includesText(
'custom_metadata validation failed: length of key',
'shows API error that is not captured by validation'
);
});
test('it can create a secret with config metadata', async function(assert) {
let enginePath = `kv-${new Date().getTime()}`;
let maxVersion = 101;
await mountSecrets.visit();
await click('[data-test-mount-type="kv"]');
await settled();
await click('[data-test-mount-next]');
await settled();
await fillIn('[data-test-input="path"]', enginePath);
await fillIn('[data-test-input="maxVersions"]', maxVersion);
await click('[data-test-input="casRequired"]');
await click('[data-test-toggle-label="Automate secret deletion"]');
await fillIn('[data-test-ttl-value="Automate secret deletion"]', '1');
await click('[data-test-mount-submit="true"]');
await settled();
await click('[data-test-configuration-tab]');
await settled();
let cas = document.querySelector('[data-test-value-div="Check-and-Set required"]').innerText;
let deleteVersionAfter = document.querySelector('[data-test-value-div="Delete version after"]').innerText;
let savedMaxVersion = document.querySelector('[data-test-value-div="Maximum versions"]').innerText;
assert.equal(
maxVersion,
savedMaxVersion,
'displays the max version set when configuring the secret-engine'
);
assert.equal(cas.trim(), 'Yes', 'displays the cas set when configuring the secret-engine');
assert.equal(
deleteVersionAfter.trim(),
'1s',
'displays the delete version after set when configuring the secret-engine'
);
});
test('it can create a secret and metadata can be created and edited', async function(assert) {
let enginePath = `kv-${new Date().getTime()}`;
let secretPath = 'metadata';
let maxVersions = 101;
await mountSecrets.visit();
await mountSecrets.enable('kv', enginePath);
await settled();
await editPage.startCreateSecret();
await editPage.path(secretPath);
await editPage.toggleMetadata();
await settled();
await fillIn('[data-test-input="maxVersions"]', maxVersions);
@ -86,7 +185,6 @@ module('Acceptance | secrets/secret/create', function(hooks) {
await settled();
await editPage.metadataTab();
await settled();
// convert to number for IE11 browserstack test
let savedMaxVersions = Number(document.querySelectorAll('[data-test-value-div]')[0].innerText);
assert.equal(
maxVersions,
@ -94,16 +192,14 @@ module('Acceptance | secrets/secret/create', function(hooks) {
'max_version displays the saved number set when creating the secret'
);
});
// ARG TOD add test here that adds custom metadata
test('it disables save when validation errors occur', async function(assert) {
let enginePath = `kv-${new Date().getTime()}`;
await mountSecrets.visit();
await mountSecrets.enable('kv', enginePath);
await settled();
await click('[data-test-secret-create="true"]');
await fillIn('[data-test-secret-path="true"]', 'beep');
await triggerKeyEvent('[data-test-secret-path="true"]', 'keyup', 65);
await editPage.startCreateSecret();
await typeIn('[data-test-secret-path="true"]', 'beep');
assert
.dom('[data-test-inline-error-message]')
.hasText(
@ -113,16 +209,15 @@ module('Acceptance | secrets/secret/create', function(hooks) {
await editPage.toggleMetadata();
await settled();
document.querySelector('#maxVersions').value = 'abc';
await triggerKeyEvent('[data-test-input="maxVersions"]', 'keyup', 65);
await typeIn('[data-test-input="maxVersions"]', 'abc');
await settled();
assert
.dom('[data-test-input="maxVersions"]')
.hasClass('has-error-border', 'shows border error on input with error');
assert.dom('[data-test-secret-save="true"]').isDisabled('Save button is disabled');
await fillIn('[data-test-input="maxVersions"]', 20);
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 fillIn('[data-test-secret-path="true"]', 'meep');
await editPage.path('meep');
await triggerKeyEvent('[data-test-secret-path="true"]', 'keyup', 65);
await click('[data-test-secret-save="true"]');
assert.equal(currentURL(), `/vault/secrets/${enginePath}/show/meep`, 'navigates to show secret');
@ -155,7 +250,7 @@ module('Acceptance | secrets/secret/create', function(hooks) {
);
});
test('version 1 performs the correct capabilities lookup', async function(assert) {
test('version 1 performs the correct capabilities lookup and does not show metadata tab', async function(assert) {
let enginePath = `kv-${new Date().getTime()}`;
let secretPath = 'foo/bar';
// mount version 1 engine
@ -171,6 +266,8 @@ module('Acceptance | secrets/secret/create', function(hooks) {
await editPage.createSecret(secretPath, 'foo', 'bar');
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');
assert.ok(showPage.editIsPresent, 'shows the edit button');
//check for metadata tab should not exist on KV version 1
assert.dom('[data-test-secret-metadata-tab]').doesNotExist('does not show metadata tab');
});
// https://github.com/hashicorp/vault/issues/5960
@ -320,7 +417,7 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.ok(showPage.editIsPresent, 'shows the edit button');
});
test('version 2 with restricted policy still allows edit', async function(assert) {
test('version 2 with restricted policy still allows edit but does not show metadata tab', async function(assert) {
let backend = 'kv-v2';
const V2_POLICY = `
path "kv-v2/metadata/*" {
@ -339,16 +436,18 @@ module('Acceptance | secrets/secret/create', function(hooks) {
]);
let userToken = consoleComponent.lastLogOutput;
// check secret edit
await writeSecret(backend, 'secret', 'foo', 'bar');
await logout.visit();
await authPage.login(userToken);
await editPage.visitEdit({ backend, id: 'secret' });
assert.notOk(editPage.hasMetadataFields, 'hides the metadata form');
await editPage.editSecret('bar', 'baz');
await settled();
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');
assert.ok(showPage.editIsPresent, 'shows the edit button');
//check for metadata tab
assert.dom('[data-test-secret-metadata-tab]').doesNotExist('does not show metadata tab');
});
test('version 2 with policy with destroy capabilities shows modal', async function(assert) {
@ -603,7 +702,9 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.ok(showPage.editIsPresent, 'shows the edit button');
await editPage.visitEdit({ backend, id: 'secret' });
assert.notOk(editPage.hasMetadataFields, 'hides the metadata form');
assert
.dom('[data-test-warning-no-read-permissions]')
.exists('shows custom warning instead of default API warning about permissions');
await editPage.editSecret('bar', 'baz');
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');

View file

@ -94,6 +94,7 @@ module('Integration | Component | secret edit', function(hooks) {
null: 'null',
float: '1.234',
},
canReadSecretData: true,
});
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);

View file

@ -3,7 +3,7 @@ import { isPresent, clickable, visitable, create, fillable } from 'ember-cli-pag
import { codeFillable } from 'vault/tests/pages/helpers/codemirror';
export default create({
...Base,
path: fillable('[data-test-secret-path]'),
path: fillable('[data-test-secret-path="true"]'),
secretKey: fillable('[data-test-secret-key]'),
secretValue: fillable('[data-test-secret-value] textarea'),
save: clickable('[data-test-secret-save]'),
@ -15,6 +15,8 @@ export default create({
toggleMetadata: clickable('[data-test-show-metadata-toggle]'),
metadataTab: clickable('[data-test-secret-metadata-tab]'),
hasMetadataFields: isPresent('[data-test-metadata-fields]'),
maxVersion: fillable('[data-test-input="maxVersions"]'),
startCreateSecret: clickable('[data-test-secret-create="true"]'),
editor: {
fillIn: codeFillable('[data-test-component="json-editor"]'),
},