From 819e4fa809b4a06f90cd21f5acd97a819ada2b6e Mon Sep 17 00:00:00 2001
From: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
Date: Fri, 29 Oct 2021 09:58:56 -0700
Subject: [PATCH] UI/ Add PostgreSQL DB (#12945)
* alphabetize DB plugin types
* adds changelog
* add postgres to database plugins
* add statement fields
* adds tests for postgres db
* add delete confirm modal to db connection
* fixes text for confirmation modal - transform
* editing tests for delete modal
* fixes tests, oracle must be last DB tested
* adds test for modal and updates old modal tests
---
changelog/12945.txt | 3 +
.../components/database-connection.hbs | 17 ++++-
.../components/transformation-edit.hbs | 2 +-
ui/app/utils/database-helpers.js | 71 +++++++++++++------
.../addon/components/confirmation-modal.js | 1 +
.../secrets/backend/database/secret-test.js | 41 +++++++++--
6 files changed, 106 insertions(+), 29 deletions(-)
create mode 100644 changelog/12945.txt
diff --git a/changelog/12945.txt b/changelog/12945.txt
new file mode 100644
index 000000000000..128378716f03
--- /dev/null
+++ b/changelog/12945.txt
@@ -0,0 +1,3 @@
+```release-note:feature
+**Postgres in the UI**: Postgres DB is now supported by the UI
+```
\ No newline at end of file
diff --git a/ui/app/templates/components/database-connection.hbs b/ui/app/templates/components/database-connection.hbs
index 69b34a7c33f7..5a032c586714 100644
--- a/ui/app/templates/components/database-connection.hbs
+++ b/ui/app/templates/components/database-connection.hbs
@@ -23,7 +23,7 @@
+
+
+ Deleting the connection means that any associated roles won't be able to generate credentials until the connection is reconfigured.
+
diff --git a/ui/app/utils/database-helpers.js b/ui/app/utils/database-helpers.js index 2702912fd90b..34df12c868c1 100644 --- a/ui/app/utils/database-helpers.js +++ b/ui/app/utils/database-helpers.js @@ -1,4 +1,24 @@ export const AVAILABLE_PLUGIN_TYPES = [ + { + value: 'elasticsearch-database-plugin', + displayName: 'Elasticsearch', + fields: [ + { attr: 'plugin_name' }, + { attr: 'name' }, + { attr: 'verify_connection' }, + { attr: 'password_policy' }, + { attr: 'url', group: 'pluginConfig' }, + { attr: 'username', group: 'pluginConfig', show: false }, + { attr: 'password', group: 'pluginConfig', show: false }, + { attr: 'ca_cert', group: 'pluginConfig' }, + { attr: 'ca_path', group: 'pluginConfig' }, + { attr: 'client_cert', group: 'pluginConfig' }, + { attr: 'client_key', group: 'pluginConfig' }, + { attr: 'tls_server_name', group: 'pluginConfig' }, + { attr: 'insecure', group: 'pluginConfig' }, + { attr: 'username_template', group: 'pluginConfig' }, + ], + }, { value: 'mongodb-database-plugin', displayName: 'MongoDB', @@ -36,8 +56,8 @@ export const AVAILABLE_PLUGIN_TYPES = [ ], }, { - value: 'mysql-database-plugin', - displayName: 'MySQL/MariaDB', + value: 'mysql-aurora-database-plugin', + displayName: 'MySQL (Aurora)', fields: [ { attr: 'plugin_name' }, { attr: 'name' }, @@ -56,8 +76,8 @@ export const AVAILABLE_PLUGIN_TYPES = [ ], }, { - value: 'mysql-aurora-database-plugin', - displayName: 'MySQL (Aurora)', + value: 'mysql-legacy-database-plugin', + displayName: 'MySQL (Legacy)', fields: [ { attr: 'plugin_name' }, { attr: 'name' }, @@ -76,8 +96,8 @@ export const AVAILABLE_PLUGIN_TYPES = [ ], }, { - value: 'mysql-rds-database-plugin', - displayName: 'MySQL (RDS)', + value: 'mysql-database-plugin', + displayName: 'MySQL/MariaDB', fields: [ { attr: 'plugin_name' }, { attr: 'name' }, @@ -96,8 +116,8 @@ export const AVAILABLE_PLUGIN_TYPES = [ ], }, { - value: 'mysql-legacy-database-plugin', - displayName: 'MySQL (Legacy)', + value: 'mysql-rds-database-plugin', + displayName: 'MySQL (RDS)', fields: [ { attr: 'plugin_name' }, { attr: 'name' }, @@ -116,28 +136,26 @@ export const AVAILABLE_PLUGIN_TYPES = [ ], }, { - value: 'elasticsearch-database-plugin', - displayName: 'Elasticsearch', + value: 'oracle-database-plugin', + displayName: 'Oracle', fields: [ { attr: 'plugin_name' }, { attr: 'name' }, { attr: 'verify_connection' }, { attr: 'password_policy' }, - { attr: 'url', group: 'pluginConfig' }, + { attr: 'connection_url', group: 'pluginConfig' }, { attr: 'username', group: 'pluginConfig', show: false }, { attr: 'password', group: 'pluginConfig', show: false }, - { attr: 'ca_cert', group: 'pluginConfig' }, - { attr: 'ca_path', group: 'pluginConfig' }, - { attr: 'client_cert', group: 'pluginConfig' }, - { attr: 'client_key', group: 'pluginConfig' }, - { attr: 'tls_server_name', group: 'pluginConfig' }, - { attr: 'insecure', group: 'pluginConfig' }, + { attr: 'max_open_connections', group: 'pluginConfig' }, + { attr: 'max_idle_connections', group: 'pluginConfig' }, + { attr: 'max_connection_lifetime', group: 'pluginConfig' }, { attr: 'username_template', group: 'pluginConfig' }, + { attr: 'root_rotation_statements', group: 'statements' }, ], }, { - value: 'oracle-database-plugin', - displayName: 'Oracle', + value: 'postgresql-database-plugin', + displayName: 'PostgreSQL', fields: [ { attr: 'plugin_name' }, { attr: 'name' }, @@ -163,25 +181,32 @@ export const ROLE_FIELDS = { export const STATEMENT_FIELDS = { static: { default: ['rotation_statements'], + 'elasticsearch-database-plugin': [], 'mongodb-database-plugin': [], 'mssql-database-plugin': [], 'mysql-database-plugin': [], 'mysql-aurora-database-plugin': [], - 'mysql-rds-database-plugin': [], 'mysql-legacy-database-plugin': [], - 'elasticsearch-database-plugin': [], + 'mysql-rds-database-plugin': [], 'oracle-database-plugin': [], + 'postgresql-database-plugin': [], }, dynamic: { default: ['creation_statements', 'revocation_statements', 'rollback_statements', 'renew_statements'], + 'elasticsearch-database-plugin': ['creation_statement'], 'mongodb-database-plugin': ['creation_statement', 'revocation_statement'], 'mssql-database-plugin': ['creation_statements', 'revocation_statements'], 'mysql-database-plugin': ['creation_statements', 'revocation_statements'], 'mysql-aurora-database-plugin': ['creation_statements', 'revocation_statements'], - 'mysql-rds-database-plugin': ['creation_statements', 'revocation_statements'], 'mysql-legacy-database-plugin': ['creation_statements', 'revocation_statements'], - 'elasticsearch-database-plugin': ['creation_statement'], + 'mysql-rds-database-plugin': ['creation_statements', 'revocation_statements'], 'oracle-database-plugin': ['creation_statements', 'revocation_statements'], + 'postgresql-database-plugin': [ + 'creation_statements', + 'revocation_statements', + 'rollback_statements', + 'renew_statements', + ], }, }; diff --git a/ui/lib/core/addon/components/confirmation-modal.js b/ui/lib/core/addon/components/confirmation-modal.js index 7f193fffb74b..981dc6942cfd 100644 --- a/ui/lib/core/addon/components/confirmation-modal.js +++ b/ui/lib/core/addon/components/confirmation-modal.js @@ -9,6 +9,7 @@ * @title="Do Dangerous Thing?" * @isActive={{isModalActive}} * @onClose={{action (mut isModalActive) false}} + * @onConfirmMsg="deleting this thing to delete." * /> * ``` * @param {function} onConfirm - onConfirm is the action that happens when user clicks onConfirm after filling in the confirmation block diff --git a/ui/tests/acceptance/secrets/backend/database/secret-test.js b/ui/tests/acceptance/secrets/backend/database/secret-test.js index 8ab2744bae4c..0cc0a456d277 100644 --- a/ui/tests/acceptance/secrets/backend/database/secret-test.js +++ b/ui/tests/acceptance/secrets/backend/database/secret-test.js @@ -185,6 +185,31 @@ const connectionTests = [ .exists(`Root rotation statements exists for ${name}`); }, }, + { + name: 'postgresql-connection', + plugin: 'postgresql-database-plugin', + url: `postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable`, + 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}`); + }, + }, + // keep oracle as last DB because it is skipped in some tests (line 285) the UI doesn't return to empty state after { name: 'oracle-connection', plugin: 'oracle-database-plugin', @@ -256,7 +281,7 @@ module('Acceptance | secrets/database/*', function(hooks) { } else { await connectionPage.connectionUrl(testCase.url); } - // skip adding oracle db connection since plugin doesn't exists + // skip adding oracle db connection since plugin doesn't exist if (testCase.plugin === 'oracle-database-plugin') { testCase.requiredFields(assert, testCase.name); continue; @@ -264,9 +289,9 @@ module('Acceptance | secrets/database/*', function(hooks) { testCase.requiredFields(assert, testCase.name); await connectionPage.toggleVerify(); await connectionPage.save(); - await connectionPage.enable(); + await settled(); assert - .dom('[data-test-modal-title]') + .dom('.modal.is-active .title') .hasText('Rotate your root credentials?', 'Modal appears asking to rotate root credentials'); await connectionPage.enable(); assert.ok( @@ -335,8 +360,9 @@ module('Acceptance | secrets/database/*', function(hooks) { // uncheck verify for the save step to work await connectionPage.toggleVerify(); await connectionPage.save(); + await settled(); assert - .dom('[data-test-modal-title]') + .dom('.modal.is-active .title') .hasText('Rotate your root credentials?', 'Modal appears asking to '); await connectionPage.enable(); assert.equal( @@ -356,6 +382,13 @@ module('Acceptance | secrets/database/*', function(hooks) { } }); 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"]', connectionDetails.id); + await click('[data-test-confirm-button]'); + await settled(); + assert.equal(currentURL(), `/vault/secrets/${backend}/list`, 'Redirects to connection list page'); assert .dom('[data-test-empty-state-title')