mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-20 06:01:10 +02:00
* adds ldap ember engine (#20786) * adds ldap as mountable and supported secrets engine (#20793) * removes active directory as mountable secrets engine (#20798) * LDAP Config Ember Data Setup (#20863) * adds secret-engine-path adapter * adds model, adapater and serializer for ldap config * adds test for ldap config adapter * addresses PR feedback * updates remaining instances of getURL in secrets-engine-path adapter * adds underscore to getURL method in kubernetes/config adapter * adds check config vars test for kubernetes/config adapter * adds comment regarding primaryKey in secrets-engine-path adapter * adds tab-page-header component for ldap secrets engine (#20941) * LDAP Config Route (#21059) * converts secret-mount-path service to ts and moves kubernetes fetch-config decorator to core addon and converts to ts * adds ldap config route * fixes withConfig import path in kubernetes roles route * updates types in ldap config route * adds unit tests for fetch-secret-config decorator * updates comments in fetch-secret-config decorator * renames fetch-secret-config decorator * LDAP Configure Page Component (#21384) * adds ldap page configure component * removes pauseTest and updates radio card selector in ldap config test * LDAP Configuration (#21430) * adds ldap configuration route * adds secrets-engine-mount-config component to core addon * adds ldap config-cta component * adds display fields to ldap configuration page and test * fixes ldap config-cta test * adds yield to secrets-engine-mount-config component * fixes tests * LDAP Overview Route and Page Component (#21579) * adds ldap overview route and page component * changes toolbar link action type for create role on overview page * LDAP Role Model, Adapter and Serializer (#21655) * adds model, adapter and serializer for ldap roles * addresses review feedback * changes ldap role type from tracked prop to attr and sets in adapter for query methods * adds assertions to verify that frontend only props are returned from query methods in ldap role adapter * LDAP Library Model, Adapter and Serializer (#21728) * adds model, adapter and serializer for ldap library * updates capitalization and punction for ldap role and library form fields * LDAP Roles Create and Edit (#21818) * moves stringify and jsonify helpers to core addon * adds validation error for ttl picker in form field component * adds ldap roles create and edit routes and page component * adds ldap mirage handler and factory for roles * adds example workflow to json editor component * adds tests for ldap page create and edit component * addresses feedback * LDAP Role Details (#22036) * adds ldap role route to pass down model to child routes * adds ldap role details route and page component * updates ldap role model capabilities checks * adds periods to error messages * removes modelFor from ldap roles edit and details routes * adds flash message on ldap role delete success * LDAP Roles (#22070) * adds ldap roles route and page component * update ldap role adapter tests and adds adapter options to query for partialErrorInfo * updates ldap role adapter based on PR feedback * adds filter-input component to core addon * updates ldap roles page to use filter-input component * updates ldap role adapter tests * LDAP Role Credentials (#22142) * adds ldap roles route and page component * update ldap role adapter tests and adds adapter options to query for partialErrorInfo * adds credentials actions to ldap roles list menu and fixes rotate action in details view * adds ldap role credentials route and page component * adds tests for ldap role credentials * LDAP Library Create and Edit (#22171) * adds ldap library create/edit routes and page component * adds ldap library create-and-edit tests and library mirage factory * updates form-field component to display validation errors and warnings for all fields * updates ldap library edit route class name * updates ldap library model interface name * adds missing period in flash message * LDAP Libraries (#22184) * updates interface and class names in ldap roles route * adds ldap libraries route and page component * fixes lint error * LDAP Library Details (#22200) * updates interface and class names in ldap roles route * adds ldap libraries route and page component * fixes lint error * adds ldap library details route and page component * LDAP Library Details Configuration (#22201) * updates interface and class names in ldap roles route * adds ldap libraries route and page component * fixes lint error * adds ldap library details route and page component * adds ldap library details configuration route and page component * updates ldap library check-in enforcement value mapping * fixes issue in code mirror modifier after merging upgrade * fixes failing database secrets test * LDAP Library Account Details (#22287) * adds route and page component for ldap library accounts * adds ldap component for checked out accounts * updates ldap library adapter tests * LDAP Library Check-out (#22289) * adds route and page component for ldap library accounts * adds ldap component for checked out accounts * adds route and page component for ldap library checkout * addresses PR feedback * LDAP Overview Cards (#22325) * adds overview cards to ldap overview route * adds create library toolbar action to ldap overview route * adds acceptance tests for ldap workflows (#22375) * Fetch Secrets Engine Config Decorator Docs (#22416) * removes uneccesary asyncs from ldap route model hooks * updates ldap overview route class name * adds documentation for fetch-secrets-engine-config decorator * add changelog * adding back external links, missed due to merge. * changelog * fix test after merging in dashboard work * Update 20790.txt --------- Co-authored-by: Angel Garbarino <angel@hashicorp.com> Co-authored-by: Angel Garbarino <Monkeychip@users.noreply.github.com>
559 lines
25 KiB
JavaScript
559 lines
25 KiB
JavaScript
/**
|
||
* Copyright (c) HashiCorp, Inc.
|
||
* SPDX-License-Identifier: BUSL-1.1
|
||
*/
|
||
|
||
import { module, test } from 'qunit';
|
||
import { setupApplicationTest } from 'ember-qunit';
|
||
import { currentURL, settled, click, visit, fillIn, typeIn } from '@ember/test-helpers';
|
||
import { create } from 'ember-cli-page-object';
|
||
import { selectChoose, clickTrigger } from 'ember-power-select/test-support/helpers';
|
||
|
||
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
|
||
import connectionPage from 'vault/tests/pages/secrets/backend/database/connection';
|
||
import rolePage from 'vault/tests/pages/secrets/backend/database/role';
|
||
import authPage from 'vault/tests/pages/auth';
|
||
import logout from 'vault/tests/pages/logout';
|
||
import searchSelect from 'vault/tests/pages/components/search-select';
|
||
import { deleteEngineCmd, mountEngineCmd, runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands';
|
||
|
||
const searchSelectComponent = create(searchSelect);
|
||
|
||
const newConnection = async (backend, plugin = 'mongodb-database-plugin') => {
|
||
const name = `connection-${Date.now()}`;
|
||
await connectionPage.visitCreate({ backend });
|
||
await connectionPage.dbPlugin(plugin);
|
||
await connectionPage.name(name);
|
||
await connectionPage.connectionUrl(`mongodb://127.0.0.1:4321/${name}`);
|
||
await connectionPage.toggleVerify();
|
||
await connectionPage.save();
|
||
await connectionPage.enable();
|
||
return name;
|
||
};
|
||
|
||
const navToConnection = async (backend, connection) => {
|
||
await visit('/vault/secrets');
|
||
await click(`[data-test-secrets-backend-link="${backend}"]`);
|
||
await click('[data-test-secret-list-tab="Connections"]');
|
||
await click(`[data-test-secret-link="${connection}"]`);
|
||
return;
|
||
};
|
||
|
||
const connectionTests = [
|
||
{
|
||
name: 'elasticsearch-connection',
|
||
plugin: 'elasticsearch-database-plugin',
|
||
elasticUser: 'username',
|
||
elasticPassword: 'password',
|
||
url: 'http://127.0.0.1:9200',
|
||
assertCount: 9,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert.dom('[data-test-input="ca_cert"]').exists(`CA certificate field exists for ${name}`);
|
||
assert.dom('[data-test-input="ca_path"]').exists(`CA path field exists for ${name}`);
|
||
assert.dom('[data-test-input="client_cert"]').exists(`Client certificate field exists for ${name}`);
|
||
assert.dom('[data-test-input="client_key"]').exists(`Client key field exists for ${name}`);
|
||
assert.dom('[data-test-input="tls_server_name"]').exists(`TLS server name field exists for ${name}`);
|
||
assert.dom('[data-test-input="insecure"]').exists(`Insecure checkbox exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-toggle-input="show-username_template"]')
|
||
.exists(`Username template toggle exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'mongodb-connection',
|
||
plugin: 'mongodb-database-plugin',
|
||
url: `mongodb://127.0.0.1:4321/test`,
|
||
assertCount: 5,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert.dom('[data-test-input="write_concern"]').exists(`Write concern field exists for ${name}`);
|
||
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'mssql-connection',
|
||
plugin: 'mssql-database-plugin',
|
||
url: `mssql://127.0.0.1:4321/test`,
|
||
assertCount: 6,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'mysql-connection',
|
||
plugin: 'mysql-database-plugin',
|
||
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
|
||
assertCount: 7,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'mysql-aurora-connection',
|
||
plugin: 'mysql-aurora-database-plugin',
|
||
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
|
||
assertCount: 7,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'mysql-rds-connection',
|
||
plugin: 'mysql-rds-database-plugin',
|
||
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
|
||
assertCount: 7,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'mysql-legacy-connection',
|
||
plugin: 'mysql-legacy-database-plugin',
|
||
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
|
||
assertCount: 7,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
},
|
||
},
|
||
{
|
||
name: 'postgresql-connection',
|
||
plugin: 'postgresql-database-plugin',
|
||
url: `postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable`,
|
||
assertCount: 7,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-toggle-input="show-username_template"]')
|
||
.exists(`Username template toggle exists for ${name}`);
|
||
},
|
||
},
|
||
];
|
||
|
||
module('Acceptance | secrets/database/*', function (hooks) {
|
||
setupApplicationTest(hooks);
|
||
|
||
hooks.beforeEach(async function () {
|
||
this.backend = `database-testing`;
|
||
await authPage.login();
|
||
return runCmd(mountEngineCmd('database', this.backend), false);
|
||
});
|
||
hooks.afterEach(async function () {
|
||
await authPage.login();
|
||
return runCmd(deleteEngineCmd(this.backend), false);
|
||
});
|
||
|
||
test('can enable the database secrets engine', async function (assert) {
|
||
const backend = `database-${Date.now()}`;
|
||
await mountSecrets.enable('database', backend);
|
||
await settled();
|
||
assert.strictEqual(
|
||
currentURL(),
|
||
`/vault/secrets/${backend}/list`,
|
||
'Mounts and redirects to connection list page'
|
||
);
|
||
assert.dom('[data-test-component="empty-state"]').exists('Empty state exists');
|
||
assert
|
||
.dom('.active[data-test-secret-list-tab="Connections"]')
|
||
.exists('Has Connections tab which is active');
|
||
await click('[data-test-tab="overview"]');
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/overview`, 'Tab links to overview page');
|
||
assert.dom('[data-test-component="empty-state"]').exists('Empty state also exists on overview page');
|
||
assert.dom('[data-test-secret-list-tab="Roles"]').exists('Has Roles tab');
|
||
await visit('/vault/secrets');
|
||
// Cleanup backend
|
||
await runCmd(deleteEngineCmd(backend), false);
|
||
});
|
||
|
||
for (const testCase of connectionTests) {
|
||
test(`database connection create and edit: ${testCase.plugin}`, async function (assert) {
|
||
assert.expect(19 + testCase.assertCount);
|
||
const backend = this.backend;
|
||
await connectionPage.visitCreate({ backend });
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/create`, 'Correct creation URL');
|
||
assert
|
||
.dom('[data-test-empty-state-title]')
|
||
.hasText('No plugin selected', 'No plugin is selected by default and empty state shows');
|
||
await connectionPage.dbPlugin(testCase.plugin);
|
||
assert.dom('[data-test-empty-state]').doesNotExist('Empty state goes away after plugin selected');
|
||
await connectionPage.name(testCase.name);
|
||
if (testCase.plugin === 'elasticsearch-database-plugin') {
|
||
await connectionPage.url(testCase.url);
|
||
await connectionPage.username(testCase.elasticUser);
|
||
await connectionPage.password(testCase.elasticPassword);
|
||
} else {
|
||
await connectionPage.connectionUrl(testCase.url);
|
||
}
|
||
testCase.requiredFields(assert, testCase.plugin);
|
||
assert.dom('[data-test-input="verify_connection"]').isChecked('verify is checked');
|
||
await connectionPage.toggleVerify();
|
||
assert.dom('[data-test-input="verify_connection"]').isNotChecked('verify is unchecked');
|
||
assert
|
||
.dom('[data-test-database-oracle-alert]')
|
||
.doesNotExist('does not show oracle alert for non-oracle plugins');
|
||
await connectionPage.save();
|
||
await settled();
|
||
assert
|
||
.dom('.modal.is-active .title')
|
||
.hasText('Rotate your root credentials?', 'Modal appears asking to rotate root credentials');
|
||
assert.dom('[data-test-enable-connection]').exists('Enable button exists');
|
||
await click('[data-test-enable-connection]');
|
||
assert.ok(
|
||
currentURL().startsWith(`/vault/secrets/${backend}/show/${testCase.name}`),
|
||
`Saves connection and takes you to show page for ${testCase.name}`
|
||
);
|
||
assert
|
||
.dom(`[data-test-row-value="Password"]`)
|
||
.doesNotExist(`Does not show Password value on show page for ${testCase.name}`);
|
||
await connectionPage.edit();
|
||
assert.ok(
|
||
currentURL().startsWith(`/vault/secrets/${backend}/edit/${testCase.name}`),
|
||
`Edit connection button and takes you to edit page for ${testCase.name}`
|
||
);
|
||
assert.dom(`[data-test-input="name"]`).hasAttribute('readonly');
|
||
assert.dom(`[data-test-input="plugin_name"]`).hasAttribute('readonly');
|
||
assert.dom('[data-test-input="password"]').doesNotExist('Password is not displayed on edit form');
|
||
assert.dom('[data-test-toggle-input="show-password"]').exists('Update password toggle exists');
|
||
|
||
assert.dom('[data-test-input="verify_connection"]').isNotChecked('verify is still unchecked');
|
||
await connectionPage.save();
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/show/${testCase.name}`);
|
||
// click "Add Role"
|
||
await connectionPage.addRole();
|
||
await settled();
|
||
assert.strictEqual(
|
||
searchSelectComponent.selectedOptions[0].text,
|
||
testCase.name,
|
||
'Database connection is pre-selected on the form'
|
||
);
|
||
await click('[data-test-database-role-cancel]');
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/list`, 'Cancel button links to list view');
|
||
});
|
||
}
|
||
test('database connection create and edit: vault-plugin-database-oracle', async function (assert) {
|
||
assert.expect(11);
|
||
// keep oracle as separate test because it behaves differently than the others
|
||
const testCase = {
|
||
name: 'oracle-connection',
|
||
plugin: 'vault-plugin-database-oracle',
|
||
url: `{{username}}/{{password}}@localhost:1521/OraDoc.localhost`,
|
||
requiredFields: async (assert, name) => {
|
||
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
|
||
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_open_connections"]')
|
||
.exists(`Max open connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_idle_connections"]')
|
||
.exists(`Max idle connections exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="max_connection_lifetime"]')
|
||
.exists(`Max connection lifetime exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-input="root_rotation_statements"]')
|
||
.exists(`Root rotation statements exists for ${name}`);
|
||
assert
|
||
.dom('[data-test-database-oracle-alert]')
|
||
.hasTextContaining(
|
||
`Warning Please ensure that your Oracle plugin has the default name of vault-plugin-database-oracle. Custom naming is not supported in the UI at this time. If the plugin is already named vault-plugin-database-oracle, disregard this warning.`,
|
||
'warning banner displays for oracle plugin name'
|
||
);
|
||
},
|
||
};
|
||
const backend = this.backend;
|
||
await connectionPage.visitCreate({ backend });
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/create`, 'Correct creation URL');
|
||
assert
|
||
.dom('[data-test-empty-state-title]')
|
||
.hasText('No plugin selected', 'No plugin is selected by default and empty state shows');
|
||
await connectionPage.dbPlugin(testCase.plugin);
|
||
assert.dom('[data-test-empty-state]').doesNotExist('Empty state goes away after plugin selected');
|
||
assert.dom('[data-test-database-oracle-alert]').exists('shows oracle alert');
|
||
await connectionPage.name(testCase.name);
|
||
await connectionPage.connectionUrl(testCase.url);
|
||
testCase.requiredFields(assert, testCase.plugin);
|
||
// Cannot save without plugin mounted
|
||
// TODO: add fake server response for fuller test coverage
|
||
});
|
||
|
||
test('Can create and delete a connection', async function (assert) {
|
||
const backend = this.backend;
|
||
const connectionDetails = {
|
||
plugin: 'mongodb-database-plugin',
|
||
id: 'horses-db',
|
||
fields: [
|
||
{ label: 'Connection name', name: 'name', value: 'horses-db' },
|
||
{ label: 'Connection URL', name: 'connection_url', value: 'mongodb://127.0.0.1:235/horses' },
|
||
{ label: 'Username', name: 'username', value: 'user', hideOnShow: true },
|
||
{ label: 'Password', name: 'password', password: 'so-secure', hideOnShow: true },
|
||
{ label: 'Write concern', name: 'write_concern' },
|
||
],
|
||
};
|
||
await visit(`/vault/secrets/${backend}/list`);
|
||
await connectionPage.createLink();
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/create`, 'Create link goes to create page');
|
||
assert
|
||
.dom('[data-test-empty-state-title]')
|
||
.hasText('No plugin selected', 'No plugin is selected by default and empty state shows');
|
||
await connectionPage.dbPlugin(connectionDetails.plugin);
|
||
assert.dom('[data-test-empty-state]').doesNotExist('Empty state goes away after plugin selected');
|
||
connectionDetails.fields.forEach(async ({ name, value }) => {
|
||
assert
|
||
.dom(`[data-test-input="${name}"]`)
|
||
.exists(`Field ${name} exists for ${connectionDetails.plugin}`);
|
||
if (value) {
|
||
await fillIn(`[data-test-input="${name}"]`, value);
|
||
}
|
||
});
|
||
// uncheck verify for the save step to work
|
||
await connectionPage.toggleVerify();
|
||
await connectionPage.save();
|
||
await settled();
|
||
assert
|
||
.dom('.modal.is-active .title')
|
||
.hasText('Rotate your root credentials?', 'Modal appears asking to ');
|
||
await connectionPage.enable();
|
||
assert.strictEqual(
|
||
currentURL(),
|
||
`/vault/secrets/${backend}/show/${connectionDetails.id}`,
|
||
'Saves connection and takes you to show page'
|
||
);
|
||
connectionDetails.fields.forEach(({ label, name, value, hideOnShow }) => {
|
||
if (hideOnShow) {
|
||
assert
|
||
.dom(`[data-test-row-value="${label}"]`)
|
||
.doesNotExist(`Does not show ${name} value on show page for ${connectionDetails.plugin}`);
|
||
} else if (!value) {
|
||
assert.dom(`[data-test-row-value="${label}"]`).hasText('Default');
|
||
} else {
|
||
assert.dom(`[data-test-row-value="${label}"]`).hasText(value);
|
||
}
|
||
});
|
||
await connectionPage.delete();
|
||
assert
|
||
.dom('.modal.is-active .title')
|
||
.hasText('Delete connection?', 'Modal appears asking to confirm delete action');
|
||
await fillIn('[data-test-confirmation-modal-input="Delete connection?"]', connectionDetails.id);
|
||
await click('[data-test-confirm-button]');
|
||
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/list`, 'Redirects to connection list page');
|
||
assert
|
||
.dom('[data-test-empty-state-title]')
|
||
.hasText('No connections in this backend', 'No connections listed because it was deleted');
|
||
});
|
||
|
||
test('buttons show up for managing connection', async function (assert) {
|
||
const backend = this.backend;
|
||
const connection = await newConnection(backend);
|
||
const CONNECTION_VIEW_ONLY = `
|
||
path "${backend}/config" {
|
||
capabilities = ["list"]
|
||
}
|
||
path "${backend}/config/*" {
|
||
capabilities = ["read"]
|
||
}
|
||
`;
|
||
const token = await runCmd(tokenWithPolicyCmd('test-policy', CONNECTION_VIEW_ONLY));
|
||
await navToConnection(backend, connection);
|
||
assert
|
||
.dom('[data-test-database-connection-delete]')
|
||
.hasText('Delete connection', 'Delete connection button exists with correct text');
|
||
assert
|
||
.dom('[data-test-database-connection-reset]')
|
||
.hasText('Reset connection', 'Reset button exists with correct text');
|
||
assert.dom('[data-test-secret-create]').hasText('Add role', 'Add role button exists with correct text');
|
||
assert.dom('[data-test-edit-link]').hasText('Edit configuration', 'Edit button exists with correct text');
|
||
await authPage.logout();
|
||
// Check with restricted permissions
|
||
await authPage.login(token);
|
||
await click('[data-test-sidebar-nav-link="Secrets engines"]');
|
||
assert.dom(`[data-test-secrets-backend-link="${backend}"]`).exists('Shows backend on secret list page');
|
||
await navToConnection(backend, connection);
|
||
assert.strictEqual(
|
||
currentURL(),
|
||
`/vault/secrets/${backend}/show/${connection}`,
|
||
'Allows reading connection'
|
||
);
|
||
assert
|
||
.dom('[data-test-database-connection-delete]')
|
||
.doesNotExist('Delete button does not show due to permissions');
|
||
assert
|
||
.dom('[data-test-database-connection-reset]')
|
||
.doesNotExist('Reset button does not show due to permissions');
|
||
assert.dom('[data-test-secret-create]').doesNotExist('Add role button does not show due to permissions');
|
||
assert.dom('[data-test-edit-link]').doesNotExist('Edit button does not show due to permissions');
|
||
await visit(`/vault/secrets/${backend}/overview`);
|
||
assert.dom('[data-test-selectable-card="Connections"]').exists('Connections card exists on overview');
|
||
assert
|
||
.dom('[data-test-selectable-card="Roles"]')
|
||
.doesNotExist('Roles card does not exist on overview w/ policy');
|
||
assert.dom('.title-number').hasText('1', 'Lists the correct number of connections');
|
||
// confirm get credentials card is an option to select. Regression bug.
|
||
await typeIn('.ember-text-field', 'blah');
|
||
assert.dom('[data-test-get-credentials]').isEnabled();
|
||
});
|
||
|
||
test('Role create form', async function (assert) {
|
||
const backend = this.backend;
|
||
// Connection needed for role fields
|
||
await newConnection(backend);
|
||
await rolePage.visitCreate({ backend });
|
||
await rolePage.name('bar');
|
||
assert
|
||
.dom('[data-test-component="empty-state"]')
|
||
.exists({ count: 2 }, 'Two empty states exist before selections made');
|
||
await clickTrigger('#database');
|
||
assert.strictEqual(searchSelectComponent.options.length, 1, 'list shows existing connections so far');
|
||
await selectChoose('#database', '.ember-power-select-option', 0);
|
||
assert
|
||
.dom('[data-test-component="empty-state"]')
|
||
.exists({ count: 2 }, 'Two empty states exist before selections made');
|
||
await rolePage.roleType('static');
|
||
assert.dom('[data-test-component="empty-state"]').doesNotExist('Empty states go away');
|
||
assert.dom('[data-test-input="username"]').exists('Username field appears for static role');
|
||
assert
|
||
.dom('[data-test-toggle-input="Rotation period"]')
|
||
.exists('Rotation period field appears for static role');
|
||
await rolePage.roleType('dynamic');
|
||
assert
|
||
.dom('[data-test-toggle-input="Generated credentials’s Time-to-Live (TTL)"]')
|
||
.exists('TTL field exists for dynamic');
|
||
assert
|
||
.dom('[data-test-toggle-input="Generated credentials’s maximum Time-to-Live (Max TTL)"]')
|
||
.exists('Max TTL field exists for dynamic');
|
||
// Real connection (actual running db) required to save role, so we aren't testing that flow yet
|
||
});
|
||
|
||
test('root and limited access', async function (assert) {
|
||
const backend = this.backend;
|
||
const NO_ROLES_POLICY = `
|
||
path "${backend}/roles/*" {
|
||
capabilities = ["delete"]
|
||
}
|
||
path "${backend}/static-roles/*" {
|
||
capabilities = ["delete"]
|
||
}
|
||
path "${backend}/config/*" {
|
||
capabilities = ["list", "create", "read", "update"]
|
||
}
|
||
path "${backend}/creds/*" {
|
||
capabilities = ["list", "create", "read", "update"]
|
||
}
|
||
`;
|
||
const token = await runCmd(tokenWithPolicyCmd('test-policy', NO_ROLES_POLICY));
|
||
|
||
// test root user flow first
|
||
await visit(`/vault/secrets/${backend}/overview`);
|
||
|
||
assert.dom('[data-test-component="empty-state"]').exists('renders empty state');
|
||
assert.dom('[data-test-secret-list-tab="Connections"]').exists('renders connections tab');
|
||
assert.dom('[data-test-secret-list-tab="Roles"]').exists('renders connections tab');
|
||
|
||
await click('[data-test-secret-create="connections"]');
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/create`);
|
||
|
||
// Login with restricted policy
|
||
await logout.visit();
|
||
await authPage.login(token);
|
||
await visit(`/vault/secrets/${backend}/overview`);
|
||
assert.dom('[data-test-tab="overview"]').exists('renders overview tab');
|
||
assert.dom('[data-test-secret-list-tab="Connections"]').exists('renders connections tab');
|
||
assert
|
||
.dom('[data-test-secret-list-tab="Roles"]')
|
||
.doesNotExist(`does not show the roles tab because it does not have permissions`);
|
||
assert
|
||
.dom('[data-test-selectable-card="Connections"]')
|
||
.exists({ count: 1 }, 'renders only the connection card');
|
||
|
||
await click('[data-test-action-text="Configure new"]');
|
||
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/create?itemType=connection`);
|
||
});
|
||
});
|