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
This commit is contained in:
parent
6f3d179635
commit
889d82aca5
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
ui: Customize MongoDB input fields on Database Secrets Engine
|
||||||
|
```
|
|
@ -52,7 +52,7 @@ export default class DatabaseListItem extends Component {
|
||||||
adapter
|
adapter
|
||||||
.rotateRootCredentials(backend, id)
|
.rotateRootCredentials(backend, id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.flashMessages.success(`Success: ${id} connection was reset`);
|
this.flashMessages.success(`Success: ${id} connection was rotated`);
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
this.flashMessages.danger(e.errors);
|
this.flashMessages.danger(e.errors);
|
||||||
|
|
|
@ -43,7 +43,11 @@ export default class DatabaseRoleEdit extends Component {
|
||||||
secret
|
secret
|
||||||
.destroyRecord()
|
.destroyRecord()
|
||||||
.then(() => {
|
.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 => {
|
.catch(e => {
|
||||||
this.flashMessages.danger(e.errors?.join('. '));
|
this.flashMessages.danger(e.errors?.join('. '));
|
||||||
|
@ -59,7 +63,11 @@ export default class DatabaseRoleEdit extends Component {
|
||||||
let path = roleSecret.type === 'static' ? 'static-roles' : 'roles';
|
let path = roleSecret.type === 'static' ? 'static-roles' : 'roles';
|
||||||
roleSecret.set('path', path);
|
roleSecret.set('path', path);
|
||||||
roleSecret.save().then(() => {
|
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.set('path', path);
|
||||||
}
|
}
|
||||||
roleSecret.save().then(() => {
|
roleSecret.save().then(() => {
|
||||||
this.router.transitionTo(SHOW_ROUTE, `role/${secretId}`);
|
try {
|
||||||
|
this.router.transitionTo(SHOW_ROUTE, `role/${secretId}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.debug(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ const STATEMENT_FIELDS = {
|
||||||
},
|
},
|
||||||
dynamic: {
|
dynamic: {
|
||||||
default: ['creation_statements', 'revocation_statements', 'rotation_statements'],
|
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;
|
const type = this.args.roleType;
|
||||||
if (!type) return null;
|
if (!type) return null;
|
||||||
const db = this.args.dbType || 'default';
|
const db = this.args.dbType || 'default';
|
||||||
const fields = ROLE_FIELDS[type][db];
|
const dbValidFields = ROLE_FIELDS[type][db];
|
||||||
if (!Array.isArray(fields)) return fields;
|
if (!Array.isArray(dbValidFields)) return dbValidFields;
|
||||||
const filtered = this.args.attrs.filter(a => {
|
return this.args.attrs.filter(a => {
|
||||||
const includes = fields.includes(a.name);
|
return dbValidFields.includes(a.name);
|
||||||
return includes;
|
|
||||||
});
|
});
|
||||||
return filtered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get statementFields() {
|
get statementFields() {
|
||||||
const type = this.args.roleType;
|
const type = this.args.roleType;
|
||||||
if (!type) return null;
|
if (!type) return null;
|
||||||
const db = this.args.dbType || 'default';
|
const db = this.args.dbType || 'default';
|
||||||
const fields = STATEMENT_FIELDS[type][db];
|
const dbValidFields = STATEMENT_FIELDS[type][db];
|
||||||
if (!Array.isArray(fields)) return fields;
|
if (!Array.isArray(dbValidFields)) return dbValidFields;
|
||||||
const filtered = this.args.attrs.filter(a => {
|
return this.args.attrs.filter(a => {
|
||||||
const includes = fields.includes(a.name);
|
return dbValidFields.includes(a.name);
|
||||||
return includes;
|
|
||||||
});
|
});
|
||||||
return filtered;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,10 +79,9 @@ export default Model.extend({
|
||||||
subText: 'x509 CA file for validating the certificate presented by the MongoDB server.',
|
subText: 'x509 CA file for validating the certificate presented by the MongoDB server.',
|
||||||
editType: 'file',
|
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.`,
|
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',
|
editType: 'stringArray',
|
||||||
theme: 'hashi short',
|
|
||||||
defaultShown: 'Default',
|
defaultShown: 'Default',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -115,15 +114,7 @@ export default Model.extend({
|
||||||
|
|
||||||
// for both create and edit fields
|
// for both create and edit fields
|
||||||
mainFields: computed('plugin_name', function() {
|
mainFields: computed('plugin_name', function() {
|
||||||
return [
|
return ['plugin_name', 'name', 'connection_url', 'verify_connection', 'password_policy', 'pluginConfig'];
|
||||||
'plugin_name',
|
|
||||||
'name',
|
|
||||||
'connection_url',
|
|
||||||
'verify_connection',
|
|
||||||
'password_policy',
|
|
||||||
'pluginConfig',
|
|
||||||
'root_rotation_statements',
|
|
||||||
];
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
showAttrs: computed('plugin_name', function() {
|
showAttrs: computed('plugin_name', function() {
|
||||||
|
@ -133,13 +124,15 @@ export default Model.extend({
|
||||||
'connection_url',
|
'connection_url',
|
||||||
'write_concern',
|
'write_concern',
|
||||||
'verify_connection',
|
'verify_connection',
|
||||||
'root_rotation_statements',
|
|
||||||
'allowed_roles',
|
'allowed_roles',
|
||||||
];
|
];
|
||||||
return expandAttributeMeta(this, f);
|
return expandAttributeMeta(this, f);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
pluginFieldGroups: computed('plugin_name', function() {
|
pluginFieldGroups: computed('plugin_name', function() {
|
||||||
|
if (!this.plugin_name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
let groups = [{ default: ['username', 'password', 'write_concern'] }];
|
let groups = [{ default: ['username', 'password', 'write_concern'] }];
|
||||||
// TODO: Get plugin options based on plugin
|
// TODO: Get plugin options based on plugin
|
||||||
groups.push({
|
groups.push({
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default Model.extend({
|
||||||
theme: 'hashi short',
|
theme: 'hashi short',
|
||||||
defaultShown: 'Default',
|
defaultShown: 'Default',
|
||||||
}),
|
}),
|
||||||
rotation_statement: attr('string', {
|
revocation_statement: attr('string', {
|
||||||
editType: 'json',
|
editType: 'json',
|
||||||
theme: 'hashi short',
|
theme: 'hashi short',
|
||||||
defaultShown: 'Default',
|
defaultShown: 'Default',
|
||||||
|
@ -72,7 +72,18 @@ export default Model.extend({
|
||||||
|
|
||||||
/* FIELD ATTRIBUTES */
|
/* FIELD ATTRIBUTES */
|
||||||
get fieldAttrs() {
|
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);
|
return expandAttributeMeta(this, fields);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -86,8 +97,8 @@ export default Model.extend({
|
||||||
'creation_statements',
|
'creation_statements',
|
||||||
'creation_statement', // only for MongoDB (styling difference)
|
'creation_statement', // only for MongoDB (styling difference)
|
||||||
'revocation_statements',
|
'revocation_statements',
|
||||||
|
'revocation_statement', // only for MongoDB (styling difference)
|
||||||
'rotation_statements',
|
'rotation_statements',
|
||||||
'rotation_statement', // only for MongoDB (styling difference)
|
|
||||||
];
|
];
|
||||||
return expandAttributeMeta(this, allRoleSettingFields);
|
return expandAttributeMeta(this, allRoleSettingFields);
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -24,12 +24,17 @@ export default RESTSerializer.extend({
|
||||||
if (payload.data.db_name) {
|
if (payload.data.db_name) {
|
||||||
database = [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 {
|
return {
|
||||||
id: payload.secret,
|
id: payload.secret,
|
||||||
name: payload.secret,
|
name: payload.secret,
|
||||||
backend: payload.backend,
|
backend: payload.backend,
|
||||||
database,
|
database,
|
||||||
path,
|
path,
|
||||||
|
creation_statement,
|
||||||
|
revocation_statement,
|
||||||
...payload.data,
|
...payload.data,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -64,6 +69,18 @@ export default RESTSerializer.extend({
|
||||||
data.db_name = db;
|
data.db_name = db;
|
||||||
delete data.database;
|
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;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
|
@ -87,7 +87,7 @@
|
||||||
{{!-- Plugin Config Section --}}
|
{{!-- Plugin Config Section --}}
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<h3 class="title is-5">Plugin config</h3>
|
<h3 class="title is-5">Plugin config</h3>
|
||||||
{{#unless @model.plugin_name}}
|
{{#unless @model.pluginFieldGroups}}
|
||||||
<EmptyState
|
<EmptyState
|
||||||
@title="No plugin selected"
|
@title="No plugin selected"
|
||||||
@message="Select a plugin type to be able to configure it."
|
@message="Select a plugin type to be able to configure it."
|
||||||
|
@ -97,7 +97,6 @@
|
||||||
{{#each-in fieldGroup as |group fields|}}
|
{{#each-in fieldGroup as |group fields|}}
|
||||||
{{#if (eq group "default")}}
|
{{#if (eq group "default")}}
|
||||||
{{#each fields as |attr|}}
|
{{#each fields as |attr|}}
|
||||||
{{!-- TODO: special password edit mode --}}
|
|
||||||
{{form-field data-test-field attr=attr model=@model}}
|
{{form-field data-test-field attr=attr model=@model}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -18,6 +18,19 @@
|
||||||
{{#if (eq @mode 'show')}}
|
{{#if (eq @mode 'show')}}
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<ToolbarActions>
|
<ToolbarActions>
|
||||||
|
{{#if @model.canDelete}}
|
||||||
|
<ConfirmAction
|
||||||
|
@buttonClasses="toolbar-link"
|
||||||
|
@onConfirmAction={{action 'delete'}}
|
||||||
|
@confirmTitle="Delete role?"
|
||||||
|
@confirmMessage="This role will be permanently deleted. You will need to recreate it to use it again."
|
||||||
|
@confirmButtonText="Delete"
|
||||||
|
data-test-database-role-delete
|
||||||
|
>
|
||||||
|
Delete role
|
||||||
|
</ConfirmAction>
|
||||||
|
<div class="toolbar-separator" />
|
||||||
|
{{/if}}
|
||||||
{{#if @model.canGenerateCredentials}}
|
{{#if @model.canGenerateCredentials}}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -28,18 +41,6 @@
|
||||||
Generate credentials
|
Generate credentials
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if @model.canDelete}}
|
|
||||||
<ConfirmAction
|
|
||||||
@buttonClasses="toolbar-link"
|
|
||||||
@onConfirmAction={{action 'delete'}}
|
|
||||||
@confirmTitle="Delete role?"
|
|
||||||
@confirmMessage="This role will be permanently deleted. You will need to re-create it to use it again."
|
|
||||||
@confirmButtonText="Delete"
|
|
||||||
data-test-database-role-delete
|
|
||||||
>
|
|
||||||
Delete role
|
|
||||||
</ConfirmAction>
|
|
||||||
{{/if}}
|
|
||||||
{{#if @model.canEditRole}}
|
{{#if @model.canEditRole}}
|
||||||
<ToolbarSecretLink
|
<ToolbarSecretLink
|
||||||
@secret={{concat 'role/' @model.id}}
|
@secret={{concat 'role/' @model.id}}
|
||||||
|
@ -53,7 +54,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ToolbarActions>
|
</ToolbarActions>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
{{#each @model.fieldAttrs as |attr|}}
|
{{#each @model.showFields as |attr|}}
|
||||||
{{#let attr.options.defaultDisplay as |defaultDisplay|}}
|
{{#let attr.options.defaultDisplay as |defaultDisplay|}}
|
||||||
{{#if (eq attr.type "object")}}
|
{{#if (eq attr.type "object")}}
|
||||||
<InfoTableRow
|
<InfoTableRow
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
@glyph="cancel-square-outline"
|
@glyph="cancel-square-outline"
|
||||||
/> No
|
/> No
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else if (and alwaysRender defaultShown)}}
|
{{else if (and (not value) (and alwaysRender defaultShown))}}
|
||||||
{{defaultShown}}
|
{{defaultShown}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if (eq type 'array')}}
|
{{#if (eq type 'array')}}
|
||||||
|
|
Loading…
Reference in New Issue