From 4b709e8b3b12e1bb6f8506003cfd82955b608f17 Mon Sep 17 00:00:00 2001
From: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
Date: Thu, 7 Oct 2021 14:00:42 -0700
Subject: [PATCH] UI/Add Elasticsearch DB (#12672)
* displays empty state if database is not supported in the UI
* adds elasticsearch db plugin
* adds changelog
* updates elasticsearch attrs
* move tls_server_name to pluginConfig group
* move role setting fields to util
* updates comments and refactors using util function
* adds tests for elasticsearch
* fixes indentation
* when local host needs https
* adds line at bottom of hbs file
---
changelog/12672.txt | 3 +
.../components/database-role-setting-form.js | 35 +---
ui/app/models/database/connection.js | 62 +++++-
ui/app/models/database/role.js | 11 +-
ui/app/styles/components/empty-state.scss | 3 +
.../components/database-connection.hbs | 179 ++++++++++--------
.../secret-list/database-list-item.hbs | 16 +-
ui/app/utils/database-role-fields.js | 41 ++++
.../templates/components/linkable-item.hbs | 2 +-
.../secrets/backend/database/secret-test.js | 34 +++-
.../database-role-setting-form-test.js | 5 +
.../secrets/backend/database/connection.js | 5 +-
12 files changed, 254 insertions(+), 142 deletions(-)
create mode 100644 changelog/12672.txt
create mode 100644 ui/app/utils/database-role-fields.js
diff --git a/changelog/12672.txt b/changelog/12672.txt
new file mode 100644
index 000000000..62ac12bdc
--- /dev/null
+++ b/changelog/12672.txt
@@ -0,0 +1,3 @@
+```release-note:feature
+**Elasticsearch in the UI**: Elasticsearch DB is now supported by the UI
+```
\ No newline at end of file
diff --git a/ui/app/components/database-role-setting-form.js b/ui/app/components/database-role-setting-form.js
index cdea6b57c..e7d9f6b4d 100644
--- a/ui/app/components/database-role-setting-form.js
+++ b/ui/app/components/database-role-setting-form.js
@@ -14,38 +14,12 @@
*/
import Component from '@glimmer/component';
+import { getStatementFields, getRoleFields } from '../utils/database-role-fields';
-// Below fields are intended to be dynamic based on type of role and db.
-// example of usage: FIELDS[roleType][db]
-const ROLE_FIELDS = {
- static: ['username', 'rotation_period'],
- dynamic: ['ttl', 'max_ttl'],
-};
-
-const STATEMENT_FIELDS = {
- static: {
- default: ['rotation_statements'],
- 'mongodb-database-plugin': [],
- 'mssql-database-plugin': [],
- 'mysql-database-plugin': [],
- 'mysql-aurora-database-plugin': [],
- 'mysql-rds-database-plugin': [],
- 'mysql-legacy-database-plugin': [],
- },
- dynamic: {
- default: ['creation_statements', 'revocation_statements', 'rollback_statements', 'renew_statements'],
- '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'],
- },
-};
export default class DatabaseRoleSettingForm extends Component {
get settingFields() {
if (!this.args.roleType) return null;
- let dbValidFields = ROLE_FIELDS[this.args.roleType];
+ let dbValidFields = getRoleFields(this.args.roleType);
return this.args.attrs.filter(a => {
return dbValidFields.includes(a.name);
});
@@ -55,10 +29,7 @@ export default class DatabaseRoleSettingForm extends Component {
const type = this.args.roleType;
const plugin = this.args.dbType;
if (!type) return null;
- let dbValidFields = STATEMENT_FIELDS[type].default;
- if (STATEMENT_FIELDS[type][plugin]) {
- dbValidFields = STATEMENT_FIELDS[type][plugin];
- }
+ let dbValidFields = getStatementFields(type, plugin);
return this.args.attrs.filter(a => {
return dbValidFields.includes(a.name);
});
diff --git a/ui/app/models/database/connection.js b/ui/app/models/database/connection.js
index e82dc5904..86ba691e0 100644
--- a/ui/app/models/database/connection.js
+++ b/ui/app/models/database/connection.js
@@ -121,6 +121,26 @@ const AVAILABLE_PLUGIN_TYPES = [
{ attr: 'root_rotation_statements', group: 'statements' },
],
},
+ {
+ 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' },
+ ],
+ },
];
/**
@@ -149,7 +169,7 @@ export default Model.extend({
}),
// required
name: attr('string', {
- label: 'Connection Name',
+ label: 'Connection name',
}),
plugin_name: attr('string', {
label: 'Database plugin',
@@ -177,22 +197,38 @@ export default Model.extend({
// common fields
connection_url: attr('string', {
- subText: 'The connection string used to connect to the database.',
+ label: 'Connection URL',
+ subText:
+ 'The connection string used to connect to the database. This allows for simple templating of username and password of the root user in the {{field_name}} format.',
}),
url: attr('string', {
- subText:
- 'The connection string used to connect to the database. This allows for simple templating of username and password of the root user.',
+ label: 'URL',
+ subText: `The URL for Elasticsearch's API ("https://localhost:9200").`,
}),
username: attr('string', {
- subText: 'Optional. The name of the user to use as the "root" user when connecting to the database.',
+ subText: `The name of the user to use as the "root" user when connecting to the database.`,
}),
password: attr('string', {
- subText:
- 'Optional. The password to use when connecting to the database. Typically used in the connection_url field via the templating directive {{password}}.',
+ subText: 'The password to use when connecting with the above username.',
editType: 'password',
}),
// optional
+ ca_cert: attr('string', {
+ label: 'CA certificate',
+ subText: `The path to a PEM-encoded CA cert file to use to verify the Elasticsearch server's identity.`,
+ }),
+ ca_path: attr('string', {
+ label: 'CA path',
+ subText: `The path to a directory of PEM-encoded CA cert files to use to verify the Elasticsearch server's identity.`,
+ }),
+ client_cert: attr('string', {
+ label: 'Client certificate',
+ subText: 'The path to the certificate for the Elasticsearch client to present for communication.',
+ }),
+ client_key: attr('string', {
+ subText: 'The path to the key for the Elasticsearch client to use for communication.',
+ }),
hosts: attr('string', {}),
host: attr('string', {}),
port: attr('string', {}),
@@ -220,6 +256,10 @@ export default Model.extend({
max_connection_lifetime: attr('string', {
defaultValue: '0s',
}),
+ insecure: attr('boolean', {
+ defaultValue: false,
+ label: 'Disable SSL verification',
+ }),
tls: attr('string', {
label: 'TLS Certificate Key',
helpText:
@@ -232,12 +272,20 @@ export default Model.extend({
'x509 CA file for validating the certificate presented by the MongoDB server. Must be PEM encoded.',
editType: 'file',
}),
+ tls_server_name: attr('string', {
+ label: 'TLS server name',
+ subText: 'If set, this name is used to set the SNI host when connecting via 1TLS.',
+ }),
root_rotation_statements: attr({
subText: `The database statements to be executed to rotate the root user's credentials. If nothing is entered, Vault will use a reasonable default.`,
editType: 'stringArray',
defaultShown: 'Default',
}),
+ isAvailablePlugin: computed('plugin_name', function() {
+ return !!AVAILABLE_PLUGIN_TYPES.find(a => a.value === this.plugin_name);
+ }),
+
showAttrs: computed('plugin_name', function() {
const fields = AVAILABLE_PLUGIN_TYPES.find(a => a.value === this.plugin_name)
.fields.filter(f => f.show !== false)
diff --git a/ui/app/models/database/role.js b/ui/app/models/database/role.js
index 17e84cf4d..3ed255a06 100644
--- a/ui/app/models/database/role.js
+++ b/ui/app/models/database/role.js
@@ -3,6 +3,7 @@ import { computed } from '@ember/object';
import { alias } from '@ember/object/computed';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
+import { getRoleFields } from '../../utils/database-role-fields';
export default Model.extend({
idPrefix: 'role/',
@@ -90,11 +91,7 @@ export default Model.extend({
get showFields() {
let fields = ['name', 'database', 'type'];
- if (this.type === 'dynamic') {
- fields = fields.concat(['ttl', 'max_ttl', 'creation_statements', 'revocation_statements']);
- } else {
- fields = fields.concat(['username', 'rotation_period']);
- }
+ fields = fields.concat(getRoleFields(this.type)).concat(['creation_statements', 'revocation_statements']);
return expandAttributeMeta(this, fields);
},
@@ -106,9 +103,9 @@ export default Model.extend({
'username',
'rotation_period',
'creation_statements',
- 'creation_statement', // only for MongoDB (styling difference)
+ 'creation_statement', // for editType: JSON
'revocation_statements',
- 'revocation_statement', // only for MongoDB (styling difference)
+ 'revocation_statement', // only for MongoDB (editType: JSON)
'rotation_statements',
'rollback_statements',
'renew_statements',
diff --git a/ui/app/styles/components/empty-state.scss b/ui/app/styles/components/empty-state.scss
index 7a90a73ea..a6da6d405 100644
--- a/ui/app/styles/components/empty-state.scss
+++ b/ui/app/styles/components/empty-state.scss
@@ -42,6 +42,8 @@
.empty-state-actions {
margin-top: $spacing-xs;
+ display: flex;
+ justify-content: space-between;
a,
.link,
@@ -54,6 +56,7 @@
> * + * {
margin-left: $spacing-s;
+ margin-right: $spacing-s;
}
}
diff --git a/ui/app/templates/components/database-connection.hbs b/ui/app/templates/components/database-connection.hbs
index 61a968316..c8231368e 100644
--- a/ui/app/templates/components/database-connection.hbs
+++ b/ui/app/templates/components/database-connection.hbs
@@ -15,69 +15,71 @@
-{{#if (eq @mode "show")}}
-