Skip to content

Commit

Permalink
backport of commit 5ba4fb3 (#29139)
Browse files Browse the repository at this point in the history
Co-authored-by: claire bontempo <[email protected]>
  • Loading branch information
1 parent 2767f8e commit 349fe58
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 25 deletions.
3 changes: 3 additions & 0 deletions changelog/29114.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
ui: Decode database url to fix editing failures for an oracle connection
```
12 changes: 0 additions & 12 deletions ui/app/helpers/decode-uri.js

This file was deleted.

10 changes: 10 additions & 0 deletions ui/app/serializers/database/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ export default RESTSerializer.extend({
...payload.data,
...payload.data.connection_details,
};

// connection_details are spread above into the main body of response so we can remove redundant data
delete response.connection_details;
if (response?.connection_url) {
// this url can include interpolated data, such as: "{{username}}/{{password}}@localhost:1521/OraDoc.localhost"
// these curly brackets are returned by the API encoded: "%7B%7Busername%7D%7D/%7B%7Bpassword%7D%7D@localhost:1521/OraDoc.localhost"
// we decode here so the UI displays and submits the url in the correct format
response.connection_url = decodeURI(response.connection_url);
}

if (payload.data.root_credentials_rotate_statements) {
response.root_rotation_statements = payload.data.root_credentials_rotate_statements;
}
Expand Down
2 changes: 1 addition & 1 deletion ui/app/templates/components/database-connection.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@
@alwaysRender={{not (is-empty-value (get @model attr.name) hasDefault=defaultDisplay)}}
@defaultShown={{defaultDisplay}}
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
@value={{if (eq attr.name "connection_url") (decode-uri (get @model attr.name)) (get @model attr.name)}}
@value={{get @model attr.name}}
/>
{{/if}}
{{/let}}
Expand Down
61 changes: 55 additions & 6 deletions ui/tests/acceptance/secrets/backend/database/secret-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { currentURL, settled, click, visit, fillIn, typeIn, waitFor } from '@ember/test-helpers';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { create } from 'ember-cli-page-object';
import { selectChoose } from 'ember-power-select/test-support';
import { clickTrigger } from 'ember-power-select/test-support/helpers';
Expand Down Expand Up @@ -226,6 +227,7 @@ const connectionTests = [

module('Acceptance | secrets/database/*', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);

hooks.beforeEach(async function () {
this.backend = `database-testing`;
Expand Down Expand Up @@ -337,9 +339,11 @@ module('Acceptance | secrets/database/*', function (hooks) {
await visit('/vault/secrets');
});
}
test('database connection create and edit: vault-plugin-database-oracle', async function (assert) {

// keep oracle as separate test because it relies on an external plugin that isn't rolled into the vault binary
// https://github.com/hashicorp/vault-plugin-database-oracle
test('database connection create: vault-plugin-database-oracle', async function (assert) {
assert.expect(11);
// keep oracle as separate test because it behaves differently than the others
const testCase = {
name: 'oracle-connection',
plugin: 'vault-plugin-database-oracle',
Expand Down Expand Up @@ -380,7 +384,52 @@ module('Acceptance | secrets/database/*', function (hooks) {
await connectionPage.connectionUrl(testCase.url);
testCase.requiredFields(assert, testCase.plugin);
// Cannot save without plugin mounted
// TODO: add fake server response for fuller test coverage
// Edit tested separately with mocked server response
});

test('database connection edit: vault-plugin-database-oracle', async function (assert) {
assert.expect(2);
const connectionName = 'oracle-connection';
// mock API so we can test edit (without mounting external oracle plugin)
this.server.get(`/${this.backend}/config/${connectionName}`, () => {
return {
request_id: 'f869f23e-15c0-389b-82ac-84035a2b6079',
lease_id: '',
renewable: false,
lease_duration: 0,
data: {
allowed_roles: ['*'],
connection_details: {
backend: 'database',
connection_url: '%7B%7Busername%7D%7D/%7B%7Bpassword%7D%7D@//localhost:1521/ORCLPDB1',
max_connection_lifetime: '0s',
max_idle_connections: 0,
max_open_connections: 3,
username: 'VAULTADMIN',
},
password_policy: '',
plugin_name: 'vault-plugin-database-oracle',
plugin_version: '',
root_credentials_rotate_statements: [],
verify_connection: true,
},
wrap_info: null,
warnings: null,
auth: null,
mount_type: 'database',
};
});

await visit(`/vault/secrets/${this.backend}/show/${connectionName}`);
const decoded = '{{username}}/{{password}}@//localhost:1521/ORCLPDB1';
assert
.dom('[data-test-row-value="Connection URL"]')
.hasText(decoded, 'connection_url is decoded in display');

await connectionPage.edit();
assert
.dom('[data-test-input="connection_url"]')
.hasValue(decoded, 'connection_url is decoded when editing');
});

test('Can create and delete a connection', async function (assert) {
Expand Down Expand Up @@ -504,17 +553,17 @@ module('Acceptance | secrets/database/*', function (hooks) {
await visit('/vault/secrets');
});

test('connection_url must be decoded', async function (assert) {
test('connection_url is decoded', async function (assert) {
const backend = this.backend;
const connection = await newConnection(
backend,
'mongodb-database-plugin',
'{{username}}/{{password}}@oracle-xe:1521/XEPDB1'
'{{username}}/{{password}}@mongo:1521/XEPDB1'
);
await navToConnection(backend, connection);
assert
.dom('[data-test-row-value="Connection URL"]')
.hasText('{{username}}/{{password}}@oracle-xe:1521/XEPDB1');
.hasText('{{username}}/{{password}}@mongo:1521/XEPDB1');
});

test('Role create form', async function (assert) {
Expand Down
113 changes: 107 additions & 6 deletions ui/tests/unit/serializers/database/connection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ module('Unit | Serializer | database/connection', function (hooks) {
const expectedResult = {
allowed_roles: ['readonly'],
backend: 'database',
connection_details: {
backend: 'database',
insecure: false,
url: 'https://localhost:9200',
username: 'root',
},
id: 'elastic-test',
insecure: false,
name: 'elastic-test',
Expand All @@ -98,4 +92,111 @@ module('Unit | Serializer | database/connection', function (hooks) {
};
assert.deepEqual(normalized, expectedResult, `Normalizes and flattens database response`);
});

test('it should normalize values for the database type (oracle)', function (assert) {
const serializer = this.owner.lookup('serializer:database/connection');
const normalized = serializer.normalizeSecrets({
request_id: 'request-id',
lease_id: '',
renewable: false,
lease_duration: 0,
data: {
allowed_roles: ['*'],
connection_details: {
backend: 'database',
connection_url: '%7B%7Busername%7D%7D/%7B%7Bpassword%7D%7D@//localhost:1521/ORCLPDB1',
max_connection_lifetime: '0s',
max_idle_connections: 0,
max_open_connections: 3,
username: 'VAULTADMIN',
},
password_policy: '',
plugin_name: 'vault-plugin-database-oracle',
plugin_version: '',
root_credentials_rotate_statements: [],
verify_connection: true,
},
wrap_info: null,
warnings: null,
auth: null,
mount_type: 'database',
backend: 'database',
id: 'oracle-test',
});
const expectedResult = {
allowed_roles: ['*'],
backend: 'database',
connection_url: '{{username}}/{{password}}@//localhost:1521/ORCLPDB1',
id: 'oracle-test',
max_connection_lifetime: '0s',
max_idle_connections: 0,
max_open_connections: 3,
name: 'oracle-test',
password_policy: '',
plugin_name: 'vault-plugin-database-oracle',
plugin_version: '',
root_credentials_rotate_statements: [],
root_rotation_statements: [],
username: 'VAULTADMIN',
verify_connection: true,
};
assert.deepEqual(normalized, expectedResult, `Normalizes and flattens database response`);
});

test('it should normalize values if some params do not exist', function (assert) {
const serializer = this.owner.lookup('serializer:database/connection');
const normalized = serializer.normalizeSecrets({
request_id: 'request-id',
lease_id: '',
renewable: false,
lease_duration: 0,
data: {
allowed_roles: ['*'],
connection_details: { backend: 'database' }, // no connection_url param intentionally
plugin_name: 'vault-postgres-db',
},
wrap_info: null,
warnings: null,
auth: null,
mount_type: 'database',
backend: 'database',
id: 'db-test',
});
const expectedResult = {
allowed_roles: ['*'],
backend: 'database',
id: 'db-test',
name: 'db-test',
plugin_name: 'vault-postgres-db',
};
assert.deepEqual(normalized, expectedResult, `Normalizes and flattens database response`);
});

test('it should fail gracefully if no connection_details', function (assert) {
const serializer = this.owner.lookup('serializer:database/connection');
const normalized = serializer.normalizeSecrets({
request_id: 'request-id',
lease_id: '',
renewable: false,
lease_duration: 0,
data: {
allowed_roles: ['*'],
plugin_name: 'vault-postgres-db',
},
wrap_info: null,
warnings: null,
auth: null,
mount_type: 'database',
backend: 'database',
id: 'db-test',
});
const expectedResult = {
allowed_roles: ['*'],
backend: 'database',
id: 'db-test',
name: 'db-test',
plugin_name: 'vault-postgres-db',
};
assert.deepEqual(normalized, expectedResult, `Normalizes and flattens database response`);
});
});

0 comments on commit 349fe58

Please sign in to comment.