From 2ef0d92c4d0850d86bed2bec581b5f78913caa2e Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Feb 2021 14:04:51 -0600 Subject: [PATCH] UI/Database Secrets Engine cleanup (#10949) * Update role toolbar, serialization for special mongo values * Only show defaultShown if no value on info table row * Remove root_rotation_statements from mongo connection fields * Wrap this.router in try/catch if in then statement * Add changelog --- changelog/10949.txt | 3 +++ ui/app/components/database-list-item.js | 2 +- ui/app/components/database-role-edit.js | 18 ++++++++++--- .../components/database-role-setting-form.js | 22 +++++++--------- ui/app/models/database/connection.js | 19 +++++--------- ui/app/models/database/role.js | 17 ++++++++++--- ui/app/serializers/database/role.js | 17 +++++++++++++ .../components/database-connection.hbs | 3 +-- .../components/database-role-edit.hbs | 25 ++++++++++--------- .../templates/components/info-table-row.hbs | 2 +- 10 files changed, 80 insertions(+), 48 deletions(-) create mode 100644 changelog/10949.txt diff --git a/changelog/10949.txt b/changelog/10949.txt new file mode 100644 index 000000000000..6613c4816c07 --- /dev/null +++ b/changelog/10949.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Customize MongoDB input fields on Database Secrets Engine +``` diff --git a/ui/app/components/database-list-item.js b/ui/app/components/database-list-item.js index 19aa96802890..410f1865227f 100644 --- a/ui/app/components/database-list-item.js +++ b/ui/app/components/database-list-item.js @@ -52,7 +52,7 @@ export default class DatabaseListItem extends Component { adapter .rotateRootCredentials(backend, id) .then(() => { - this.flashMessages.success(`Success: ${id} connection was reset`); + this.flashMessages.success(`Success: ${id} connection was rotated`); }) .catch(e => { this.flashMessages.danger(e.errors); diff --git a/ui/app/components/database-role-edit.js b/ui/app/components/database-role-edit.js index 113fbee4362c..93eff2b3f30f 100644 --- a/ui/app/components/database-role-edit.js +++ b/ui/app/components/database-role-edit.js @@ -43,7 +43,11 @@ export default class DatabaseRoleEdit extends Component { secret .destroyRecord() .then(() => { - this.router.transitionTo(LIST_ROOT_ROUTE, backend, { queryParams: { tab: 'role' } }); + try { + this.router.transitionTo(LIST_ROOT_ROUTE, backend, { queryParams: { tab: 'role' } }); + } catch (e) { + console.debug(e); + } }) .catch(e => { this.flashMessages.danger(e.errors?.join('. ')); @@ -59,7 +63,11 @@ export default class DatabaseRoleEdit extends Component { let path = roleSecret.type === 'static' ? 'static-roles' : 'roles'; roleSecret.set('path', path); roleSecret.save().then(() => { - this.router.transitionTo(SHOW_ROUTE, `role/${secretId}`); + try { + this.router.transitionTo(SHOW_ROUTE, `role/${secretId}`); + } catch (e) { + console.debug(e); + } }); } @@ -75,7 +83,11 @@ export default class DatabaseRoleEdit extends Component { roleSecret.set('path', path); } roleSecret.save().then(() => { - this.router.transitionTo(SHOW_ROUTE, `role/${secretId}`); + try { + this.router.transitionTo(SHOW_ROUTE, `role/${secretId}`); + } catch (e) { + console.debug(e); + } }); } } diff --git a/ui/app/components/database-role-setting-form.js b/ui/app/components/database-role-setting-form.js index 97dc091ba787..44ba2f8f2eb0 100644 --- a/ui/app/components/database-role-setting-form.js +++ b/ui/app/components/database-role-setting-form.js @@ -35,7 +35,7 @@ const STATEMENT_FIELDS = { }, dynamic: { default: ['creation_statements', 'revocation_statements', 'rotation_statements'], - 'mongodb-database-plugin': ['creation_statement'], + 'mongodb-database-plugin': ['creation_statement', 'revocation_statement'], }, }; @@ -44,25 +44,21 @@ export default class DatabaseRoleSettingForm extends Component { const type = this.args.roleType; if (!type) return null; const db = this.args.dbType || 'default'; - const fields = ROLE_FIELDS[type][db]; - if (!Array.isArray(fields)) return fields; - const filtered = this.args.attrs.filter(a => { - const includes = fields.includes(a.name); - return includes; + const dbValidFields = ROLE_FIELDS[type][db]; + if (!Array.isArray(dbValidFields)) return dbValidFields; + return this.args.attrs.filter(a => { + return dbValidFields.includes(a.name); }); - return filtered; } get statementFields() { const type = this.args.roleType; if (!type) return null; const db = this.args.dbType || 'default'; - const fields = STATEMENT_FIELDS[type][db]; - if (!Array.isArray(fields)) return fields; - const filtered = this.args.attrs.filter(a => { - const includes = fields.includes(a.name); - return includes; + const dbValidFields = STATEMENT_FIELDS[type][db]; + if (!Array.isArray(dbValidFields)) return dbValidFields; + return this.args.attrs.filter(a => { + return dbValidFields.includes(a.name); }); - return filtered; } } diff --git a/ui/app/models/database/connection.js b/ui/app/models/database/connection.js index 976ea45c7f4c..f5b2bf23b899 100644 --- a/ui/app/models/database/connection.js +++ b/ui/app/models/database/connection.js @@ -79,10 +79,9 @@ export default Model.extend({ subText: 'x509 CA file for validating the certificate presented by the MongoDB server.', editType: 'file', }), - root_rotation_statements: attr('string', { + 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: 'json', - theme: 'hashi short', + editType: 'stringArray', defaultShown: 'Default', }), @@ -115,15 +114,7 @@ export default Model.extend({ // for both create and edit fields mainFields: computed('plugin_name', function() { - return [ - 'plugin_name', - 'name', - 'connection_url', - 'verify_connection', - 'password_policy', - 'pluginConfig', - 'root_rotation_statements', - ]; + return ['plugin_name', 'name', 'connection_url', 'verify_connection', 'password_policy', 'pluginConfig']; }), showAttrs: computed('plugin_name', function() { @@ -133,13 +124,15 @@ export default Model.extend({ 'connection_url', 'write_concern', 'verify_connection', - 'root_rotation_statements', 'allowed_roles', ]; return expandAttributeMeta(this, f); }), pluginFieldGroups: computed('plugin_name', function() { + if (!this.plugin_name) { + return null; + } let groups = [{ default: ['username', 'password', 'write_concern'] }]; // TODO: Get plugin options based on plugin groups.push({ diff --git a/ui/app/models/database/role.js b/ui/app/models/database/role.js index 70fa06fda623..ce6521def60b 100644 --- a/ui/app/models/database/role.js +++ b/ui/app/models/database/role.js @@ -64,7 +64,7 @@ export default Model.extend({ theme: 'hashi short', defaultShown: 'Default', }), - rotation_statement: attr('string', { + revocation_statement: attr('string', { editType: 'json', theme: 'hashi short', defaultShown: 'Default', @@ -72,7 +72,18 @@ export default Model.extend({ /* FIELD ATTRIBUTES */ get fieldAttrs() { - let fields = ['database', 'name', 'type']; + // Main fields on edit/create form + let fields = ['name', 'database', 'type']; + return expandAttributeMeta(this, fields); + }, + + 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']); + } return expandAttributeMeta(this, fields); }, @@ -86,8 +97,8 @@ export default Model.extend({ 'creation_statements', 'creation_statement', // only for MongoDB (styling difference) 'revocation_statements', + 'revocation_statement', // only for MongoDB (styling difference) 'rotation_statements', - 'rotation_statement', // only for MongoDB (styling difference) ]; return expandAttributeMeta(this, allRoleSettingFields); }), diff --git a/ui/app/serializers/database/role.js b/ui/app/serializers/database/role.js index 1cd87ab5378c..e395f3c337ed 100644 --- a/ui/app/serializers/database/role.js +++ b/ui/app/serializers/database/role.js @@ -24,12 +24,17 @@ export default RESTSerializer.extend({ if (payload.data.db_name) { database = [payload.data.db_name]; } + // Copy to singular for MongoDB + const creation_statement = payload.data.creation_statements[0]; + const revocation_statement = payload.data.revocation_statements[0]; return { id: payload.secret, name: payload.secret, backend: payload.backend, database, path, + creation_statement, + revocation_statement, ...payload.data, }; }, @@ -64,6 +69,18 @@ export default RESTSerializer.extend({ data.db_name = db; delete data.database; } + // This is necessary because the input for MongoDB is a json string + // rather than an array, so we transpose that here + if (data.creation_statement) { + const singleStatement = data.creation_statement; + data.creation_statements = [singleStatement]; + delete data.creation_statement; + } + if (data.revocation_statement) { + const singleStatement = data.revocation_statement; + data.revocation_statements = [singleStatement]; + delete data.revocation_statement; + } return data; }, diff --git a/ui/app/templates/components/database-connection.hbs b/ui/app/templates/components/database-connection.hbs index d79cbbef9715..b51d6f0e0607 100644 --- a/ui/app/templates/components/database-connection.hbs +++ b/ui/app/templates/components/database-connection.hbs @@ -87,7 +87,7 @@ {{!-- Plugin Config Section --}}

Plugin config

- {{#unless @model.plugin_name}} + {{#unless @model.pluginFieldGroups}} - {{#if @model.canGenerateCredentials}} - - {{/if}} {{#if @model.canDelete}} Delete role +
+ {{/if}} + {{#if @model.canGenerateCredentials}} + {{/if}} {{#if @model.canEditRole}} - {{#each @model.fieldAttrs as |attr|}} + {{#each @model.showFields as |attr|}} {{#let attr.options.defaultDisplay as |defaultDisplay|}} {{#if (eq attr.type "object")}} No {{/if}} - {{else if (and alwaysRender defaultShown)}} + {{else if (and (not value) (and alwaysRender defaultShown))}} {{defaultShown}} {{else}} {{#if (eq type 'array')}}