Skip to content

Commit

Permalink
UI: Database fixes (#24947) (#25043)
Browse files Browse the repository at this point in the history
  • Loading branch information
hashishaw authored Jan 25, 2024
1 parent ab782c7 commit d799184
Show file tree
Hide file tree
Showing 15 changed files with 610 additions and 44 deletions.
3 changes: 3 additions & 0 deletions changelog/24947.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
ui: Fixed minor bugs with database secrets engine
```
1 change: 1 addition & 0 deletions ui/app/adapters/database/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export default ApplicationAdapter.extend({
return {
data: {
id,
name: id,
...data,
},
};
Expand Down
2 changes: 1 addition & 1 deletion ui/app/adapters/database/role.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default ApplicationAdapter.extend({

async _updateAllowedRoles(store, { role, backend, db, type = 'add' }) {
const connection = await store.queryRecord('database/connection', { backend, id: db });
const roles = [...connection.allowed_roles];
const roles = [...(connection.allowed_roles || [])];
const allowedRoles = type === 'add' ? addToArray([roles, role]) : removeFromArray([roles, role]);
connection.allowed_roles = allowedRoles;
return connection.save();
Expand Down
2 changes: 0 additions & 2 deletions ui/app/components/database-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ export default class DatabaseConnectionEdit extends Component {
async handleCreateConnection(evt) {
evt.preventDefault();
const secret = this.args.model;
const secretId = secret.name;
secret.set('id', secretId);
secret
.save()
.then(() => {
Expand Down
4 changes: 1 addition & 3 deletions ui/app/models/database/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ export default Model.extend({
label: 'Connection will be verified',
defaultValue: true,
}),
allowed_roles: attr('array', {
readOnly: true,
}),
allowed_roles: attr('array'),
password_policy: attr('string', {
label: 'Use custom password policy',
editType: 'optionalText',
Expand Down
5 changes: 3 additions & 2 deletions ui/app/serializers/database/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default RESTSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
const nullResponses = ['updateRecord', 'createRecord', 'deleteRecord'];
const connections = nullResponses.includes(requestType)
? { name: id, backend: payload.backend }
? { name: payload.data.name, backend: payload.data.backend }
: this.normalizeSecrets(payload);
const { modelName } = primaryModelClass;
let transformedPayload = { [modelName]: connections };
Expand All @@ -66,7 +66,8 @@ export default RESTSerializer.extend({
// filter data to only allow plugin specific attrs
const allowedAttributes = Object.keys(data).filter((dataAttrs) => pluginAttributes.includes(dataAttrs));
for (const key in data) {
if (!allowedAttributes.includes(key)) {
// All connections allow allowed_roles but it's not shown on the form
if (key !== 'allowed_roles' && !allowedAttributes.includes(key)) {
delete data[key];
}
}
Expand Down
1 change: 1 addition & 0 deletions ui/app/templates/components/database-connection.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@
@isActive={{this.showSaveModal}}
@type="info"
@showCloseButton={{false}}
data-test-database-rotate-root-modal
>
<section class="modal-card-body">
<p class="has-bottom-margin-s">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<AlertBanner
@type="warning"
@message="You will not be able to access these credentials later, so please copy them now."
data-test-warning
data-test-credentials-warning
/>
{{/if}}
{{! DYNAMIC ROLE }}
Expand Down
2 changes: 1 addition & 1 deletion ui/lib/core/addon/components/modal.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
~}}

<EmberWormhole @to="modal-wormhole">
<div class="{{this.modalClass}} {{if this.isActive 'is-active'}}" aria-modal="true" data-test-modal-div>
<div class="{{this.modalClass}} {{if this.isActive 'is-active'}}" aria-modal="true" data-test-modal-div ...attributes>
<div class="modal-background" role="button" {{on "click" @onClose}} data-test-modal-background={{@title}}></div>
<div class="modal-card">
<header class="modal-card-head">
Expand Down
24 changes: 24 additions & 0 deletions ui/mirage/factories/database-connection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { Factory } from 'ember-cli-mirage';

// For the purposes of testing, we only use a subset of fields relevant to mysql
export default Factory.extend({
backend: 'database',
name: 'connection',
plugin_name: 'mysql-database-plugin',
verify_connection: true,
connection_url: '{{username}}:{{password}}@tcp(127.0.0.1:33060)/',
username: 'admin',
max_open_connections: 4,
max_idle_connections: 0,
max_connection_lifetime: '0s',
allowed_roles: () => [],
root_rotation_statements: () => [
'SELECT user from mysql.user',
"GRANT ALL PRIVILEGES ON *.* to 'sudo'@'%'",
],
});
124 changes: 124 additions & 0 deletions ui/mirage/handlers/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { Response } from 'miragejs';

export default function (server) {
const getRecord = (schema, req, dbKey) => {
const { backend, name } = req.params;
const record = schema.db[dbKey].findBy({ name, backend });
if (record) {
delete record.backend;
delete record.id;
}
return record ? { data: record } : new Response(404, {}, { errors: [] });
};
const createOrUpdateRecord = (schema, req, key) => {
const { backend, name } = req.params;
const payload = JSON.parse(req.requestBody);
const record = schema[key].findOrCreateBy({ name, backend });
record.update(payload);
return new Response(204);
};
const deleteRecord = (schema, req, dbKey) => {
const { name } = req.params;
const record = schema.db[dbKey].findBy({ name });
if (record) {
schema.db[dbKey].remove(record.id);
}
return new Response(204);
};

// Connection mgmt
server.get('/:backend/config/:name', (schema, req) => {
return getRecord(schema, req, 'database/connections');
});
server.get('/:backend/config', (schema) => {
const keys = schema.db['databaseConnections'].map((record) => record.name);
if (!keys.length) {
return new Response(404, {}, { errors: [] });
}
return {
data: {
keys,
},
};
});
server.post('/:backend/config/:name', (schema, req) => {
const { name } = req.params;
const { username } = JSON.parse(req.requestBody);
if (name === 'bad-connection') {
return new Response(
500,
{},
{
errors: [
`error creating database object: error verifying - ping: Error 1045 (28000): Access denied for user '${username}'@'192.168.65.1' (using password: YES)`,
],
}
);
}
return createOrUpdateRecord(schema, req, 'database/connections');
});
server.delete('/:backend/config/:name', (schema, req) => {
return deleteRecord(schema, req, 'database-connection');
});
// Rotate root
server.post('/:backend/rotate-root/:name', (schema, req) => {
const { name } = req.params;
if (name === 'fail-rotate') {
return new Response(
500,
{},
{
errors: [
"1 error occurred:\n\t* failed to update user: failed to change password: Error 1045 (28000): Access denied for user 'admin'@'%' (using password: YES)\n\n",
],
}
);
}
return new Response(204);
});

// Generate credentials
server.get('/:backend/creds/:role', (schema, req) => {
const { role } = req.params;
if (role === 'static-role') {
// static creds
return {
request_id: 'static-1234',
lease_id: '',
renewable: false,
lease_duration: 0,
data: {
last_vault_rotation: '2024-01-18T10:45:47.227193-06:00',
password: 'generated-password',
rotation_period: 86400,
ttl: 3600,
username: 'static-username',
},
wrap_info: null,
warnings: null,
auth: null,
mount_type: 'database',
};
}
// dynamic creds
return {
request_id: 'dynamic-1234',
lease_id: `database/creds/${role}/abcd`,
renewable: true,
lease_duration: 3600,
data: {
password: 'generated-password',
username: 'generated-username',
},
wrap_info: null,
warnings: null,
auth: null,
mount_type: 'database',
};
});
}
32 changes: 0 additions & 32 deletions ui/mirage/handlers/db.js

This file was deleted.

4 changes: 2 additions & 2 deletions ui/mirage/handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
// individual lookup done in mirage config
import base from './base';
import clients from './clients';
import db from './db';
import database from './database';
import kms from './kms';
import mfaConfig from './mfa-config';
import mfaLogin from './mfa-login';
import oidcConfig from './oidc-config';
import hcpLink from './hcp-link';
import kubernetes from './kubernetes';

export { base, clients, db, kms, mfaConfig, mfaLogin, oidcConfig, hcpLink, kubernetes };
export { base, clients, database, kms, mfaConfig, mfaLogin, oidcConfig, hcpLink, kubernetes };
Loading

0 comments on commit d799184

Please sign in to comment.