@@ -54,7 +55,7 @@
Public key
-
+
@@ -64,7 +65,7 @@
class="styled"
@checked={{@model.generateSigningKey}}
{{on "change" (fn (mut @model.generateSigningKey) (not @model.generateSigningKey))}}
- data-test-ssh-input="generate-signing-key-checkbox"
+ data-test-input="generate-signing-key-checkbox"
/>
diff --git a/ui/lib/core/addon/components/secret-list-header.hbs b/ui/lib/core/addon/components/secret-list-header.hbs
index 10d4c0562f..3df6ba0894 100644
--- a/ui/lib/core/addon/components/secret-list-header.hbs
+++ b/ui/lib/core/addon/components/secret-list-header.hbs
@@ -78,6 +78,7 @@
@route="vault.cluster.secrets.backend.list-root"
@model={{@model.id}}
@current-when="vault.cluster.secrets.backend.list-root vault.cluster.secrets.backend.list"
+ data-test-tab={{@model.id}}
>
{{capitalize (pluralize options.item)}}
diff --git a/ui/tests/acceptance/secrets/backend/aws/aws-configuration-test.js b/ui/tests/acceptance/secrets/backend/aws/aws-configuration-test.js
index 8982c76ee5..ab7cb8bfd1 100644
--- a/ui/tests/acceptance/secrets/backend/aws/aws-configuration-test.js
+++ b/ui/tests/acceptance/secrets/backend/aws/aws-configuration-test.js
@@ -71,7 +71,7 @@ module('Acceptance | aws | configuration', function (hooks) {
await enablePage.enable('aws', path);
await click(SES.configTab);
await visit(`/vault/settings/secrets/configure/${path}`);
- assert.dom('[data-test-not-found]').exists('shows page-error');
+ assert.dom(GENERAL.notFound).exists('shows page-error');
// cleanup
await runCmd(`delete sys/mounts/${path}`);
});
diff --git a/ui/tests/acceptance/secrets/backend/configuration/edit-test.js b/ui/tests/acceptance/secrets/backend/configuration/edit-test.js
deleted file mode 100644
index f19287f6b0..0000000000
--- a/ui/tests/acceptance/secrets/backend/configuration/edit-test.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright (c) HashiCorp, Inc.
- * SPDX-License-Identifier: BUSL-1.1
- */
-
-import { click } from '@ember/test-helpers';
-import { module, test } from 'qunit';
-import { setupApplicationTest } from 'ember-qunit';
-import { v4 as uuidv4 } from 'uuid';
-import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
-import { runCmd } from 'vault/tests/helpers/commands';
-import authPage from 'vault/tests/pages/auth';
-import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
-import { create } from 'ember-cli-page-object';
-import fm from 'vault/tests/pages/components/flash-message';
-const flashMessage = create(fm);
-
-module('Acceptance | secrets configuration | edit', function (hooks) {
- setupApplicationTest(hooks);
-
- hooks.beforeEach(function () {
- this.uid = uuidv4();
- return authPage.login();
- });
-
- test('it configures ssh ca', async function (assert) {
- const path = `ssh-configure-${this.uid}`;
- await enablePage.enable('ssh', path);
- await click(SES.configTab);
- await click(SES.configure);
- assert
- .dom(SES.ssh.sshInput('generate-signing-key-checkbox'))
- .isChecked('generate_signing_key defaults to true');
- await click(SES.ssh.sshInput('generate-signing-key-checkbox'));
- await click(SES.ssh.sshInput('configure-submit'));
- assert.strictEqual(
- flashMessage.latestMessage,
- 'missing public_key',
- 'renders warning flash message for failed save'
- );
- await click(SES.ssh.sshInput('generate-signing-key-checkbox'));
- await click(SES.ssh.sshInput('configure-submit'));
- assert.dom(SES.ssh.sshInput('public-key')).exists('renders public key after saving config');
- // cleanup
- await runCmd(`delete sys/mounts/${path}`);
- });
-});
diff --git a/ui/tests/acceptance/secrets/backend/ssh/ssh-configuration-test.js b/ui/tests/acceptance/secrets/backend/ssh/configuration-test.js
similarity index 56%
rename from ui/tests/acceptance/secrets/backend/ssh/ssh-configuration-test.js
rename to ui/tests/acceptance/secrets/backend/ssh/configuration-test.js
index 9fb8544726..3f04d31319 100644
--- a/ui/tests/acceptance/secrets/backend/ssh/ssh-configuration-test.js
+++ b/ui/tests/acceptance/secrets/backend/ssh/configuration-test.js
@@ -7,6 +7,7 @@ import { click, fillIn, currentURL, waitFor, visit } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { v4 as uuidv4 } from 'uuid';
+import { spy } from 'sinon';
import authPage from 'vault/tests/pages/auth';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
@@ -22,6 +23,10 @@ module('Acceptance | ssh | configuration', function (hooks) {
setupMirage(hooks);
hooks.beforeEach(function () {
+ const flash = this.owner.lookup('service:flash-messages');
+ this.flashDangerSpy = spy(flash, 'danger');
+ this.store = this.owner.lookup('service:store');
+
this.uid = uuidv4();
return authPage.login();
});
@@ -46,12 +51,12 @@ module('Acceptance | ssh | configuration', function (hooks) {
await enablePage.enable('ssh', sshPath);
await click(SES.configTab);
await visit(`/vault/settings/secrets/configure/${sshPath}`);
- assert.dom('[data-test-not-found]').exists('shows page-error');
+ assert.dom(GENERAL.notFound).exists('shows page-error');
// cleanup
await runCmd(`delete sys/mounts/${sshPath}`);
});
- test('it should show a public key after saving default configuration', async function (assert) {
+ test('it should show a public key after saving default configuration and allows you to delete public key', async function (assert) {
const sshPath = `ssh-${this.uid}`;
await enablePage.enable('ssh', sshPath);
await click(SES.configTab);
@@ -61,32 +66,61 @@ module('Acceptance | ssh | configuration', function (hooks) {
`/vault/secrets/${sshPath}/configuration/edit`,
'transitions to the configuration page'
);
- assert.dom(SES.ssh.configureForm).exists('renders ssh configuration form');
-
// default has generate CA checked so we just submit the form
- await click(SES.ssh.sshInput('configure-submit'));
+ await click(SES.ssh.save);
assert.strictEqual(
currentURL(),
`/vault/secrets/${sshPath}/configuration/edit`,
'stays on configuration form page.'
);
-
- await waitFor(SES.ssh.sshInput('public-key'));
- assert.dom(SES.ssh.sshInput('public-key')).exists('renders the public key input on form page');
- assert.dom(SES.ssh.sshInput('public-key')).hasClass('masked-input', 'public key is masked');
-
+ // There is a delay in the backend for the public key to be generated, wait for it to complete by checking that the public key is displayed
+ await waitFor(GENERAL.inputByAttr('public-key'));
+ assert.dom(GENERAL.inputByAttr('public-key')).hasText('***********', 'public key is masked');
+ assert
+ .dom(SES.ssh.editConfigSection)
+ .exists('renders the edit configuration section of the form and not the create part');
+ // delete Public key
+ await click(SES.ssh.deletePublicKey);
+ assert.dom(GENERAL.confirmMessage).hasText('This will remove the CA certificate information.');
+ await click(GENERAL.confirmButton);
+ assert.strictEqual(
+ currentURL(),
+ `/vault/secrets/${sshPath}/configuration/edit`,
+ 'after deleting public key stays on edit page'
+ );
+ assert.dom(GENERAL.maskedInput('privateKey')).hasNoText('Private key is empty and reset');
+ assert.dom(GENERAL.inputByAttr('publicKey')).hasNoText('Public key is empty and reset');
+ assert
+ .dom(GENERAL.inputByAttr('generate-signing-key-checkbox'))
+ .isNotChecked('Generate signing key is unchecked');
await click(SES.viewBackend);
await click(SES.configTab);
assert
- .dom(`[data-test-value-div="Public key"] [data-test-masked-input]`)
- .hasText('***********', 'value for Public key is on config details and is masked');
- assert
- .dom(GENERAL.infoRowValue('Generate signing key'))
- .hasText('Yes', 'value for Generate signing key displays default of true/yes.');
+ .dom(GENERAL.emptyStateTitle)
+ .hasText('SSH not configured', 'after deleting public key SSH is no longer configured');
// cleanup
await runCmd(`delete sys/mounts/${sshPath}`);
});
+ test('it displays error if generate Signing key is not checked and no public and private keys', async function (assert) {
+ const path = `ssh-configure-${this.uid}`;
+ await enablePage.enable('ssh', path);
+ await click(SES.configTab);
+ await click(SES.configure);
+ assert
+ .dom(GENERAL.inputByAttr('generate-signing-key-checkbox'))
+ .isChecked('generate_signing_key defaults to true');
+ await click(GENERAL.inputByAttr('generate-signing-key-checkbox'));
+ await click(SES.ssh.save);
+ assert.true(this.flashDangerSpy.calledWith('missing public_key'), 'Danger flash message is displayed');
+ // visit the details page and confirm the public key is not shown
+ await visit(`/vault/secrets/${path}/configuration`);
+ assert.dom(GENERAL.infoRowLabel('Public key')).doesNotExist('Public Key label does not exist');
+ assert.dom(GENERAL.emptyStateTitle).hasText('SSH not configured', 'SSH not configured');
+ // cleanup
+ await runCmd(`delete sys/mounts/${path}`);
+ });
+
test('it should show API error when SSH configuration read fails', async function (assert) {
assert.expect(1);
const path = `ssh-${this.uid}`;
diff --git a/ui/tests/acceptance/secrets/backend/ssh/role-test.js b/ui/tests/acceptance/secrets/backend/ssh/role-test.js
deleted file mode 100644
index f4d3ab8d93..0000000000
--- a/ui/tests/acceptance/secrets/backend/ssh/role-test.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * Copyright (c) HashiCorp, Inc.
- * SPDX-License-Identifier: BUSL-1.1
- */
-
-import { currentRouteName, settled } from '@ember/test-helpers';
-import { module, test } from 'qunit';
-import { setupApplicationTest } from 'ember-qunit';
-import { v4 as uuidv4 } from 'uuid';
-
-import editPage from 'vault/tests/pages/secrets/backend/ssh/edit-role';
-import showPage from 'vault/tests/pages/secrets/backend/ssh/show';
-import generatePage from 'vault/tests/pages/secrets/backend/ssh/generate-otp';
-import listPage from 'vault/tests/pages/secrets/backend/list';
-import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
-import authPage from 'vault/tests/pages/auth';
-
-module('Acceptance | secrets/ssh', function (hooks) {
- setupApplicationTest(hooks);
-
- hooks.beforeEach(function () {
- this.uid = uuidv4();
- return authPage.login();
- });
-
- const mountAndNav = async (uid) => {
- const path = `ssh-${uid}`;
- await enablePage.enable('ssh', path);
- await settled();
- await editPage.visitRoot({ backend: path });
- await settled();
- return path;
- };
-
- test('it creates a role and redirects', async function (assert) {
- assert.expect(5);
- const path = await mountAndNav(this.uid);
- await editPage.createOTPRole('role');
- await settled();
- assert.strictEqual(
- currentRouteName(),
- 'vault.cluster.secrets.backend.show',
- 'redirects to the show page'
- );
- assert.ok(showPage.generateIsPresent, 'shows the generate button');
-
- await showPage.visit({ backend: path, id: 'role' });
- await settled();
- await showPage.generate();
- await settled();
- assert.strictEqual(
- currentRouteName(),
- 'vault.cluster.secrets.backend.credentials',
- 'navs to the credentials page'
- );
-
- await listPage.visitRoot({ backend: path });
- await settled();
- assert.strictEqual(listPage.secrets.length, 1, 'shows role in the list');
- const secret = listPage.secrets.objectAt(0);
- await secret.menuToggle();
- assert.dom('.hds-dropdown li').exists({ count: 5 }, 'Renders 5 popup menu items');
- });
-
- test('it deletes a role', async function (assert) {
- assert.expect(2);
- const path = await mountAndNav(this.uid);
- await editPage.createOTPRole('role');
- await settled();
- await showPage.visit({ backend: path, id: 'role' });
- await settled();
- await showPage.deleteRole();
- await settled();
- assert.strictEqual(
- currentRouteName(),
- 'vault.cluster.secrets.backend.list-root',
- 'redirects to list page'
- );
- assert.ok(listPage.backendIsEmpty, 'no roles listed');
- });
-
- test('it generates an OTP', async function (assert) {
- assert.expect(6);
- const path = await mountAndNav(this.uid);
- await editPage.createOTPRole('role');
- await settled();
- assert.strictEqual(
- currentRouteName(),
- 'vault.cluster.secrets.backend.show',
- 'redirects to the show page'
- );
- assert.ok(showPage.generateIsPresent, 'shows the generate button');
-
- await showPage.visit({ backend: path, id: 'role' });
- await settled();
- await showPage.generate();
- await settled();
- assert.strictEqual(
- currentRouteName(),
- 'vault.cluster.secrets.backend.credentials',
- 'navs to the credentials page'
- );
-
- await generatePage.generateOTP();
- await settled();
- assert.ok(generatePage.warningIsPresent, 'shows warning');
- await generatePage.back();
- await settled();
- assert.ok(generatePage.userIsPresent, 'clears generate, shows user input');
- assert.ok(generatePage.ipIsPresent, 'clears generate, shows ip input');
- });
-});
diff --git a/ui/tests/acceptance/secrets/backend/ssh/roles-test.js b/ui/tests/acceptance/secrets/backend/ssh/roles-test.js
new file mode 100644
index 0000000000..1756be783d
--- /dev/null
+++ b/ui/tests/acceptance/secrets/backend/ssh/roles-test.js
@@ -0,0 +1,223 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { click, fillIn, currentURL, find, settled, waitUntil, currentRouteName } from '@ember/test-helpers';
+import { module, test } from 'qunit';
+import { setupApplicationTest } from 'ember-qunit';
+import { v4 as uuidv4 } from 'uuid';
+
+import authPage from 'vault/tests/pages/auth';
+import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
+import listPage from 'vault/tests/pages/secrets/backend/list';
+import editPage from 'vault/tests/pages/secrets/backend/ssh/edit-role';
+import showPage from 'vault/tests/pages/secrets/backend/ssh/show';
+import generatePage from 'vault/tests/pages/secrets/backend/ssh/generate-otp';
+import { runCmd } from 'vault/tests/helpers/commands';
+import { GENERAL } from 'vault/tests/helpers/general-selectors';
+import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
+
+// There is duplication within this test suite. The duplication occurred because two test suites both testing ssh roles were merged.
+// refactoring the tests to remove the duplication would be a good next step as well as removing the tests/pages.
+
+module('Acceptance | ssh | roles', function (hooks) {
+ setupApplicationTest(hooks);
+
+ hooks.beforeEach(function () {
+ this.uid = uuidv4();
+ return authPage.login();
+ });
+
+ const PUB_KEY = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCn9p5dHNr4aU4R2W7ln+efzO5N2Cdv/SXk6zbCcvhWcblWMjkXf802B0PbKvf6cJIzM/Xalb3qz1cK+UUjCSEAQWefk6YmfzbOikfc5EHaSKUqDdE+HlsGPvl42rjCr28qYfuYh031YfwEQGEAIEypo7OyAj+38NLbHAQxDxuaReee1YCOV5rqWGtEgl2VtP5kG+QEBza4ZfeglS85f/GGTvZC4Jq1GX+wgmFxIPnd6/mUXa4ecoR0QMfOAzzvPm4ajcNCQORfHLQKAcmiBYMiyQJoU+fYpi9CJGT1jWTmR99yBkrSg6yitI2qqXyrpwAbhNGrM0Fw0WpWxh66N9Xp meirish@Macintosh-3.local`;
+
+ const ROLES = [
+ {
+ type: 'ca',
+ name: 'carole',
+ credsRoute: 'vault.cluster.secrets.backend.sign',
+ async fillInCreate() {
+ await click(GENERAL.inputByAttr('allowUserCertificates'));
+ },
+ async fillInGenerate() {
+ await fillIn(GENERAL.inputByAttr('publicKey'), PUB_KEY);
+ await click('[data-test-toggle-button]');
+
+ await click(GENERAL.ttl.toggle('TTL'));
+ await fillIn(GENERAL.selectByAttr('ttl-unit'), 'm');
+
+ document.querySelector(GENERAL.ttl.input('TTL')).value = 30;
+ },
+ assertBeforeGenerate(assert) {
+ assert.dom('[data-test-form-field-from-model]').exists('renders the FormFieldFromModel');
+ const value = document.querySelector('[data-test-ttl-value="TTL"]').value;
+ // confirms that the actions are correctly being passed down to the FormFieldFromModel component
+ assert.strictEqual(value, '30', 'renders action updateTtl');
+ },
+ assertAfterGenerate(assert, sshPath) {
+ assert.strictEqual(
+ currentURL(),
+ `/vault/secrets/${sshPath}/sign/${this.name}`,
+ 'ca sign url is correct'
+ );
+ assert.dom('[data-test-row-label="Signed key"]').exists({ count: 1 }, 'renders the signed key');
+ assert
+ .dom('[data-test-row-value="Signed key"]')
+ .exists({ count: 1 }, "renders the signed key's value");
+ assert.dom('[data-test-row-label="Serial number"]').exists({ count: 1 }, 'renders the serial');
+ assert.dom('[data-test-row-value="Serial number"]').exists({ count: 1 }, 'renders the serial value');
+ },
+ },
+ {
+ type: 'otp',
+ name: 'otprole',
+ credsRoute: 'vault.cluster.secrets.backend.credentials',
+ async fillInCreate() {
+ await fillIn(GENERAL.inputByAttr('defaultUser'), 'admin');
+ await click(GENERAL.toggleGroup('Options'));
+ await fillIn(GENERAL.inputByAttr('cidrList'), '1.2.3.4/32');
+ },
+ async fillInGenerate() {
+ await fillIn(GENERAL.inputByAttr('username'), 'admin');
+ await fillIn(GENERAL.inputByAttr('ip'), '1.2.3.4');
+ },
+ assertAfterGenerate(assert, sshPath) {
+ assert.strictEqual(
+ currentURL(),
+ `/vault/secrets/${sshPath}/credentials/${this.name}`,
+ 'otp credential url is correct'
+ );
+ assert.dom(GENERAL.infoRowLabel('Key')).exists({ count: 1 }, 'renders the key');
+ assert.dom('[data-test-masked-input]').exists({ count: 1 }, 'renders mask for key value');
+ assert.dom(GENERAL.infoRowLabel('Port')).exists({ count: 1 }, 'renders the port');
+ assert.dom('[data-test-row-value="Port"]').exists({ count: 1 }, "renders the port's value");
+ },
+ },
+ ];
+ test('it creates roles, generates keys and deletes roles', async function (assert) {
+ assert.expect(28);
+ const sshPath = `ssh-${this.uid}`;
+ await enablePage.enable('ssh', sshPath);
+ await click(SES.configTab);
+ await click(SES.configure);
+ // default has generate CA checked so we just submit the form
+ await click(SES.ssh.save);
+ await click(SES.viewBackend);
+
+ for (const role of ROLES) {
+ // create a role
+ await click(SES.createSecret);
+ assert.dom(SES.secretHeader).includesText('SSH Role', `${role.type}: renders the create page`);
+
+ await fillIn(GENERAL.inputByAttr('name'), role.name);
+ await fillIn(GENERAL.inputByAttr('keyType'), role.type);
+ await role.fillInCreate();
+ await settled();
+
+ // save the role
+ await click(SES.ssh.createRole);
+ await waitUntil(() => currentURL() === `/vault/secrets/${sshPath}/show/${role.name}`); // flaky without this
+ assert.strictEqual(
+ currentURL(),
+ `/vault/secrets/${sshPath}/show/${role.name}`,
+ `${role.type}: navigates to the show page on creation`
+ );
+
+ // sign a key with this role
+ await click(SES.generateLink);
+ assert.strictEqual(currentRouteName(), role.credsRoute, 'navigates to the credentials page');
+ await role.fillInGenerate();
+ if (role.type === 'ca') {
+ await settled();
+ role.assertBeforeGenerate(assert);
+ }
+
+ // generate creds
+ await click(GENERAL.saveButton);
+ await settled(); // eslint-disable-line
+ role.assertAfterGenerate(assert, sshPath);
+
+ // click the "Back" button
+ await click(SES.backButton);
+ assert.dom('[data-test-secret-generate-form]').exists(`${role.type}: back takes you back to the form`);
+
+ await click(GENERAL.cancelButton);
+ assert.strictEqual(
+ currentURL(),
+ `/vault/secrets/${sshPath}/list`,
+ `${role.type}: cancel takes you to ssh index`
+ );
+ assert.dom(SES.secretLink(role.name)).exists(`${role.type}: role shows in the list`);
+ const secret = listPage.secrets.objectAt(0);
+ await secret.menuToggle();
+ assert.dom('.hds-dropdown li').exists({ count: 5 }, 'Renders 5 popup menu items');
+
+ // and delete from the popup list menu
+ await waitUntil(() => find(SES.ssh.deleteRole)); // flaky without
+ await click(SES.ssh.deleteRole);
+ await click(GENERAL.confirmButton);
+ assert.dom(SES.secretLink(role.name)).doesNotExist(`${role.type}: role is no longer in the list`);
+ }
+ // cleanup
+ await runCmd(`delete sys/mounts/${sshPath}`);
+ });
+ module('Acceptance | ssh | otp role', function () {
+ test('it deletes a role from list view', async function (assert) {
+ assert.expect(2);
+ const path = `ssh-${this.uid}`;
+ await enablePage.enable('ssh', path);
+ await settled();
+ await editPage.visitRoot({ backend: path });
+ await editPage.createOTPRole('role');
+ await settled();
+ await showPage.visit({ backend: path, id: 'role' });
+ await settled();
+ await showPage.deleteRole();
+ await settled();
+ assert.strictEqual(
+ currentRouteName(),
+ 'vault.cluster.secrets.backend.list-root',
+ 'redirects to list page'
+ );
+ assert.ok(listPage.backendIsEmpty, 'no roles listed');
+ // cleanup
+ await runCmd(`delete sys/mounts/${path}`);
+ });
+
+ test('it generates an OTP', async function (assert) {
+ assert.expect(6);
+ const path = `ssh-${this.uid}`;
+ await enablePage.enable('ssh', path);
+ await settled();
+ await editPage.visitRoot({ backend: path });
+ await editPage.createOTPRole('role');
+ await settled();
+ assert.strictEqual(
+ currentRouteName(),
+ 'vault.cluster.secrets.backend.show',
+ 'redirects to the show page'
+ );
+ assert.ok(showPage.generateIsPresent, 'shows the generate button');
+
+ await showPage.visit({ backend: path, id: 'role' });
+ await settled();
+ await showPage.generate();
+ await settled();
+ assert.strictEqual(
+ currentRouteName(),
+ 'vault.cluster.secrets.backend.credentials',
+ 'navs to the credentials page'
+ );
+
+ await generatePage.generateOTP();
+ await settled();
+ assert.ok(generatePage.warningIsPresent, 'shows warning');
+ await generatePage.back();
+ await settled();
+ assert.ok(generatePage.userIsPresent, 'clears generate, shows user input');
+ assert.ok(generatePage.ipIsPresent, 'clears generate, shows ip input');
+ // cleanup
+ await runCmd(`delete sys/mounts/${path}`);
+ });
+ });
+});
diff --git a/ui/tests/acceptance/secrets/backend/ssh/ssh-test.js b/ui/tests/acceptance/secrets/backend/ssh/ssh-test.js
deleted file mode 100644
index c53132c6e5..0000000000
--- a/ui/tests/acceptance/secrets/backend/ssh/ssh-test.js
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * Copyright (c) HashiCorp, Inc.
- * SPDX-License-Identifier: BUSL-1.1
- */
-
-import {
- click,
- fillIn,
- currentURL,
- find,
- settled,
- waitUntil,
- currentRouteName,
- waitFor,
-} from '@ember/test-helpers';
-import { module, test } from 'qunit';
-import { setupApplicationTest } from 'ember-qunit';
-import { v4 as uuidv4 } from 'uuid';
-
-import authPage from 'vault/tests/pages/auth';
-import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
-import { GENERAL } from 'vault/tests/helpers/general-selectors';
-
-module('Acceptance | ssh secret backend', function (hooks) {
- setupApplicationTest(hooks);
-
- hooks.beforeEach(function () {
- this.uid = uuidv4();
- return authPage.login();
- });
-
- const PUB_KEY = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCn9p5dHNr4aU4R2W7ln+efzO5N2Cdv/SXk6zbCcvhWcblWMjkXf802B0PbKvf6cJIzM/Xalb3qz1cK+UUjCSEAQWefk6YmfzbOikfc5EHaSKUqDdE+HlsGPvl42rjCr28qYfuYh031YfwEQGEAIEypo7OyAj+38NLbHAQxDxuaReee1YCOV5rqWGtEgl2VtP5kG+QEBza4ZfeglS85f/GGTvZC4Jq1GX+wgmFxIPnd6/mUXa4ecoR0QMfOAzzvPm4ajcNCQORfHLQKAcmiBYMiyQJoU+fYpi9CJGT1jWTmR99yBkrSg6yitI2qqXyrpwAbhNGrM0Fw0WpWxh66N9Xp meirish@Macintosh-3.local`;
-
- const ROLES = [
- {
- type: 'ca',
- name: 'carole',
- credsRoute: 'vault.cluster.secrets.backend.sign',
- async fillInCreate() {
- await click('[data-test-input="allowUserCertificates"]');
- },
- async fillInGenerate() {
- await fillIn('[data-test-input="publicKey"]', PUB_KEY);
- await click('[data-test-toggle-button]');
-
- await click('[data-test-toggle-label="TTL"]');
- await fillIn('[data-test-select="ttl-unit"]', 'm');
-
- document.querySelector('[data-test-ttl-value="TTL"]').value = 30;
- },
- assertBeforeGenerate(assert) {
- assert.dom('[data-test-form-field-from-model]').exists('renders the FormFieldFromModel');
- const value = document.querySelector('[data-test-ttl-value="TTL"]').value;
- // confirms that the actions are correctly being passed down to the FormFieldFromModel component
- assert.strictEqual(value, '30', 'renders action updateTtl');
- },
- assertAfterGenerate(assert, sshPath) {
- assert.strictEqual(
- currentURL(),
- `/vault/secrets/${sshPath}/sign/${this.name}`,
- 'ca sign url is correct'
- );
- assert.dom('[data-test-row-label="Signed key"]').exists({ count: 1 }, 'renders the signed key');
- assert
- .dom('[data-test-row-value="Signed key"]')
- .exists({ count: 1 }, "renders the signed key's value");
- assert.dom('[data-test-row-label="Serial number"]').exists({ count: 1 }, 'renders the serial');
- assert.dom('[data-test-row-value="Serial number"]').exists({ count: 1 }, 'renders the serial value');
- },
- },
- {
- type: 'otp',
- name: 'otprole',
- credsRoute: 'vault.cluster.secrets.backend.credentials',
- async fillInCreate() {
- await fillIn('[data-test-input="defaultUser"]', 'admin');
- await click('[data-test-toggle-group="Options"]');
- await fillIn('[data-test-input="cidrList"]', '1.2.3.4/32');
- },
- async fillInGenerate() {
- await fillIn('[data-test-input="username"]', 'admin');
- await fillIn('[data-test-input="ip"]', '1.2.3.4');
- },
- assertAfterGenerate(assert, sshPath) {
- assert.strictEqual(
- currentURL(),
- `/vault/secrets/${sshPath}/credentials/${this.name}`,
- 'otp credential url is correct'
- );
- assert.dom('[data-test-row-label="Key"]').exists({ count: 1 }, 'renders the key');
- assert.dom('[data-test-masked-input]').exists({ count: 1 }, 'renders mask for key value');
- assert.dom('[data-test-row-label="Port"]').exists({ count: 1 }, 'renders the port');
- assert.dom('[data-test-row-value="Port"]').exists({ count: 1 }, "renders the port's value");
- },
- },
- ];
- test('ssh backend', async function (assert) {
- assert.expect(30);
- const sshPath = `ssh-${this.uid}`;
-
- await enablePage.enable('ssh', sshPath);
- await settled();
- await click('[data-test-configuration-tab]');
-
- await click('[data-test-secret-backend-configure]');
-
- assert.strictEqual(
- currentURL(),
- `/vault/secrets/${sshPath}/configuration/edit`,
- 'transitions to the configuration page'
- );
- assert.dom('[data-test-ssh-configure-form]').exists('renders the empty configuration form');
-
- // default has generate CA checked so we just submit the form
- await click('[data-test-ssh-input="configure-submit"]');
-
- await waitFor('[data-test-ssh-input="public-key"]');
- assert.dom('[data-test-ssh-input="public-key"]').exists();
- await click('[data-test-backend-view-link]');
-
- assert.strictEqual(currentURL(), `/vault/secrets/${sshPath}/list`, `redirects to ssh index`);
-
- for (const role of ROLES) {
- // create a role
- await click('[data-test-secret-create]');
-
- assert
- .dom('[data-test-secret-header]')
- .includesText('SSH Role', `${role.type}: renders the create page`);
-
- await fillIn('[data-test-input="name"]', role.name);
- await fillIn('[data-test-input="keyType"]', role.type);
- await role.fillInCreate();
- await settled();
-
- // save the role
- await click('[data-test-role-ssh-create]');
- await waitUntil(() => currentURL() === `/vault/secrets/${sshPath}/show/${role.name}`); // flaky without this
- assert.strictEqual(
- currentURL(),
- `/vault/secrets/${sshPath}/show/${role.name}`,
- `${role.type}: navigates to the show page on creation`
- );
-
- // sign a key with this role
- await click('[data-test-backend-credentials]');
- assert.strictEqual(currentRouteName(), role.credsRoute);
- await role.fillInGenerate();
- if (role.type === 'ca') {
- await settled();
- role.assertBeforeGenerate(assert);
- }
-
- // generate creds
- await click(GENERAL.saveButton);
- await settled(); // eslint-disable-line
- role.assertAfterGenerate(assert, sshPath);
-
- // click the "Back" button
- await click('[data-test-back-button]');
-
- assert.dom('[data-test-secret-generate-form]').exists(`${role.type}: back takes you back to the form`);
-
- await click(GENERAL.cancelButton);
-
- assert.strictEqual(
- currentURL(),
- `/vault/secrets/${sshPath}/list`,
- `${role.type}: cancel takes you to ssh index`
- );
- assert.dom(`[data-test-secret-link="${role.name}"]`).exists(`${role.type}: role shows in the list`);
-
- //and delete
- await click(`[data-test-secret-link="${role.name}"] [data-test-popup-menu-trigger]`);
- await waitUntil(() => find('[data-test-ssh-role-delete]')); // flaky without
- await click(`[data-test-ssh-role-delete]`);
- await click(`[data-test-confirm-button]`);
- assert
- .dom(`[data-test-secret-link="${role.name}"]`)
- .doesNotExist(`${role.type}: role is no longer in the list`);
- }
- });
-});
diff --git a/ui/tests/helpers/general-selectors.ts b/ui/tests/helpers/general-selectors.ts
index 1876f8fa72..dec99d1297 100644
--- a/ui/tests/helpers/general-selectors.ts
+++ b/ui/tests/helpers/general-selectors.ts
@@ -26,6 +26,7 @@ export const GENERAL = {
filterInputExplicitSearch: '[data-test-filter-input-explicit-search]',
confirmModalInput: '[data-test-confirmation-modal-input]',
confirmButton: '[data-test-confirm-button]',
+ confirmMessage: '[data-test-confirm-action-message]',
confirmTrigger: '[data-test-confirm-action-trigger]',
emptyStateTitle: '[data-test-empty-state-title]',
emptyStateSubtitle: '[data-test-empty-state-subtitle]',
@@ -42,6 +43,7 @@ export const GENERAL = {
inputByAttr: (attr: string) => `[data-test-input="${attr}"]`,
selectByAttr: (attr: string) => `[data-test-select="${attr}"]`,
toggleInput: (attr: string) => `[data-test-toggle-input="${attr}"]`,
+ toggleGroup: (attr: string) => `[data-test-toggle-group="${attr}"]`,
ttl: {
toggle: (attr: string) => `[data-test-toggle-label="${attr}"]`,
input: (attr: string) => `[data-test-ttl-value="${attr}"]`,
@@ -50,12 +52,14 @@ export const GENERAL = {
validation: (attr: string) => `[data-test-field-validation=${attr}]`,
validationWarning: (attr: string) => `[data-test-validation-warning=${attr}]`,
messageError: '[data-test-message-error]',
+ notFound: '[data-test-not-found]',
pageError: {
error: '[data-test-page-error]',
errorTitle: (httpStatus: number) => `[data-test-page-error-title="${httpStatus}"]`,
errorMessage: '[data-test-page-error-message]',
errorDetails: '[data-test-page-error-details]',
},
+ inlineError: '[data-test-inline-error-message]',
kvObjectEditor: {
deleteRow: (idx = 0) => `[data-test-kv-delete-row="${idx}"]`,
},
diff --git a/ui/tests/helpers/secret-engine/secret-engine-helpers.js b/ui/tests/helpers/secret-engine/secret-engine-helpers.js
index 092eabb853..87195cd912 100644
--- a/ui/tests/helpers/secret-engine/secret-engine-helpers.js
+++ b/ui/tests/helpers/secret-engine/secret-engine-helpers.js
@@ -21,7 +21,7 @@ const createAwsRootConfig = (store, backend) => {
id: backend,
modelName: 'aws/root-config',
data: {
- backend: backend,
+ backend,
region: 'us-west-2',
access_key: '123-key',
iam_endpoint: 'iam-endpoint',
@@ -36,8 +36,7 @@ const createSshCaConfig = (store, backend) => {
id: backend,
modelName: 'ssh/ca-config',
data: {
- backend: backend,
- public_key: 'public-key',
+ backend,
generate_signing_key: true,
},
});
diff --git a/ui/tests/helpers/secret-engine/secret-engine-selectors.ts b/ui/tests/helpers/secret-engine/secret-engine-selectors.ts
index 5dbc343074..80a035c9e3 100644
--- a/ui/tests/helpers/secret-engine/secret-engine-selectors.ts
+++ b/ui/tests/helpers/secret-engine/secret-engine-selectors.ts
@@ -26,7 +26,11 @@ export const SECRET_ENGINE_SELECTORS = {
delete: (role: string) => `[data-test-aws-role-delete="${role}"]`,
},
ssh: {
- configureForm: '[data-test-ssh-configure-form]',
- sshInput: (name: string) => `[data-test-ssh-input="${name}"]`,
+ configureForm: '[data-test-configure-form]',
+ editConfigSection: '[data-test-edit-config-section]',
+ deletePublicKey: '[data-test-delete-public-key]',
+ save: '[data-test-configure-save-button]',
+ createRole: '[data-test-role-ssh-create]',
+ deleteRole: '[data-test-ssh-role-delete]',
},
};
diff --git a/ui/tests/integration/components/secret-engine/configure-ssh-test.js b/ui/tests/integration/components/secret-engine/configure-ssh-test.js
new file mode 100644
index 0000000000..c401165a97
--- /dev/null
+++ b/ui/tests/integration/components/secret-engine/configure-ssh-test.js
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'vault/tests/helpers';
+import { render, click } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import { GENERAL } from 'vault/tests/helpers/general-selectors';
+import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
+import { createConfig } from 'vault/tests/helpers/secret-engine/secret-engine-helpers';
+import sinon from 'sinon';
+
+module('Integration | Component | SecretEngine/configure-ssh', function (hooks) {
+ setupRenderingTest(hooks);
+
+ hooks.beforeEach(function () {
+ this.store = this.owner.lookup('service:store');
+ this.model = createConfig(this.store, 'ssh-test', 'ssh');
+ this.saveConfig = sinon.stub();
+ });
+
+ test('it shows create fields if not configured', async function (assert) {
+ await render(hbs`
+