diff --git a/ui/app/components/generate-credentials-database.hbs b/ui/app/components/generate-credentials-database.hbs
index 3df000fc27..b2c900f36d 100644
--- a/ui/app/components/generate-credentials-database.hbs
+++ b/ui/app/components/generate-credentials-database.hbs
@@ -41,22 +41,22 @@
-
-
+
+
{{/if}}
{{! STATIC ROLE }}
{{#if (and (eq @roleType "static") @model.username)}}
-
+
{{/if}}
diff --git a/ui/app/components/secret-engine/list.ts b/ui/app/components/secret-engine/list.ts
index 35c5aa13d7..1c39757fab 100644
--- a/ui/app/components/secret-engine/list.ts
+++ b/ui/app/components/secret-engine/list.ts
@@ -62,23 +62,23 @@ export default class SecretEngineList extends Component {
{
key: 'accessor',
label: 'Accessor',
- width: '175px',
+ width: '205px',
},
{
key: 'description',
label: 'Description',
- width: '300px',
+ width: '320px',
},
{
key: 'running_plugin_version',
label: 'Version',
isSortable: true,
- width: '170px',
+ width: '175px',
},
{
key: 'popupMenu',
label: 'Action',
- width: '75px',
+ width: '80px',
},
];
diff --git a/ui/app/components/toolbar-secret-link.hbs b/ui/app/components/toolbar-secret-link.hbs
index 9cbc026931..f324566827 100644
--- a/ui/app/components/toolbar-secret-link.hbs
+++ b/ui/app/components/toolbar-secret-link.hbs
@@ -14,5 +14,5 @@
...attributes
>
{{yield}}
-
+
\ No newline at end of file
diff --git a/ui/app/routes/vault/cluster/secrets/backend/credentials.js b/ui/app/routes/vault/cluster/secrets/backend/credentials.js
index 69a8dbead4..c30c2e8948 100644
--- a/ui/app/routes/vault/cluster/secrets/backend/credentials.js
+++ b/ui/app/routes/vault/cluster/secrets/backend/credentials.js
@@ -5,7 +5,6 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
-import ControlGroupError from 'vault/lib/control-group-error';
const SUPPORTED_DYNAMIC_BACKENDS = ['database', 'ssh', 'aws', 'totp'];
@@ -13,7 +12,6 @@ export default Route.extend({
templateName: 'vault/cluster/secrets/backend/credentials',
pathHelp: service('path-help'),
router: service(),
- store: service(),
api: service(),
beforeModel(transition) {
@@ -34,21 +32,42 @@ export default Route.extend({
}
},
- getDatabaseCredential(backend, secret, roleType = '') {
- return this.store.queryRecord('database/credential', { backend, secret, roleType }).catch((error) => {
- if (error instanceof ControlGroupError) {
- throw error;
+ async getDatabaseCredential(backend, secret, roleType = '') {
+ try {
+ if (roleType === 'static') {
+ const { last_vault_rotation, lease_duration, data } =
+ await this.api.secrets.databaseReadStaticRoleCredentials(secret, backend);
+ return {
+ last_vault_rotation,
+ lease_duration,
+ ...data,
+ };
+ } else {
+ const { data, lease_id, lease_duration } = await this.api.secrets.databaseGenerateCredentials(
+ secret,
+ backend
+ );
+ return {
+ ...data,
+ lease_id,
+ lease_duration,
+ };
+ }
+ } catch (error) {
+ const { response } = await this.api.parseError(error);
+ if (response.isControlGroupError) {
+ throw response;
}
// Unless it's a control group error, we want to pass back error info
// so we can render it on the GenerateCredentialsDatabase component
- return error;
- });
+ return response;
+ }
},
async getAwsRole(backend, id) {
try {
- const role = await this.store.queryRecord('role-aws', { backend, id });
- return role;
+ const { data } = await this.api.secrets.awsReadRole(id, backend);
+ return data;
} catch (e) {
// swallow error, non-essential data
return;
@@ -57,8 +76,8 @@ export default Route.extend({
async getTotpKey(backend, keyName) {
try {
- const resp = await this.api.secrets.totpReadKey(keyName, backend);
- return resp.data || {};
+ const { data } = await this.api.secrets.totpReadKey(keyName, backend);
+ return data || {};
} catch (e) {
// swallow error, non-essential data
return {};
@@ -86,19 +105,11 @@ export default Route.extend({
roleName: role,
roleType,
dbCred,
- awsRoleType: awsRole?.credentialType,
+ awsRoleType: awsRole?.credential_type,
};
},
resetController(controller) {
controller.reset();
},
-
- actions: {
- willTransition() {
- // we do not want to save any of the credential information in the store.
- // once the user navigates away from this page, remove all credential info.
- this.store.unloadAll('database/credential');
- },
- },
});
diff --git a/ui/app/routes/vault/cluster/secrets/backend/overview.js b/ui/app/routes/vault/cluster/secrets/backend/overview.js
index f3b7c21d6d..3ce1ae2587 100644
--- a/ui/app/routes/vault/cluster/secrets/backend/overview.js
+++ b/ui/app/routes/vault/cluster/secrets/backend/overview.js
@@ -7,54 +7,96 @@ import Route from '@ember/routing/route';
import { hash } from 'rsvp';
import { service } from '@ember/service';
import { getEnginePathParam } from 'vault/utils/backend-route-helpers';
+import {
+ SecretsApiDatabaseListStaticRolesListEnum,
+ SecretsApiDatabaseListRolesListEnum,
+ SecretsApiDatabaseListConnectionsListEnum,
+} from '@hashicorp/vault-client-typescript';
export default Route.extend({
- store: service(),
+ capabilities: service(),
+ api: service(),
type: '',
+ // this only grabs connections for current db backend, only used to populate # of connections
async fetchConnection(queryOptions) {
try {
- return await this.store.query('database/connection', queryOptions);
- } catch (e) {
- return e.httpStatus;
+ const { keys } = await this.api.secrets.databaseListConnections(
+ queryOptions.backend,
+ SecretsApiDatabaseListConnectionsListEnum.TRUE
+ );
+ return keys;
+ } catch (error) {
+ const { status } = await this.api.parseError(error);
+ if (status === 404) {
+ return status;
+ }
}
},
+ // this grabs both dynamic and static roles for current db backend, only used to populate # of roles
async fetchAllRoles(queryOptions) {
try {
- return await this.store.query('database/role', queryOptions);
- } catch (e) {
- return e.httpStatus;
- }
- },
+ const roles = [];
+ const { backend } = queryOptions;
+ const [staticResp, dynamicResp] = await Promise.allSettled([
+ this.api.secrets.databaseListStaticRoles(backend, SecretsApiDatabaseListStaticRolesListEnum.TRUE),
+ this.api.secrets.databaseListRoles(backend, SecretsApiDatabaseListRolesListEnum.TRUE),
+ ]);
- pathQuery(backend, endpoint) {
- return {
- id: `${backend}/${endpoint}/`,
- };
+ if (staticResp.status === 'rejected' && dynamicResp.status === 'rejected') {
+ const { response: staticError, status: staticStatus } = await this.api.parseError(staticResp.reason);
+ const { response: dynamicError, status: dynamicStatus } = await this.api.parseError(
+ dynamicResp.reason
+ );
+ if (staticError?.isControlGroupError) {
+ throw staticError;
+ }
+ throw staticStatus < dynamicStatus ? dynamicError : staticError;
+ } else {
+ if (staticResp.value) {
+ roles.push(...staticResp.value.keys);
+ }
+ if (dynamicResp.value) {
+ roles.push(...dynamicResp.value.keys);
+ }
+ return roles;
+ }
+ } catch (error) {
+ const { status } = await this.api.parseError(error);
+ if (status === 404) {
+ return status;
+ }
+ }
},
async fetchCapabilitiesRole(queryOptions) {
- return this.store.queryRecord('capabilities', this.pathQuery(queryOptions.backend, 'roles'));
+ const paths = [this.capabilities.pathFor('databaseRoles', { backend: queryOptions.backend })];
+ const capabilities = paths ? await this.capabilities.fetch(paths) : {};
+ return capabilities[paths[0]];
},
async fetchCapabilitiesStaticRole(queryOptions) {
- return this.store.queryRecord('capabilities', this.pathQuery(queryOptions.backend, 'static-roles'));
+ const paths = [this.capabilities.pathFor('databaseStaticRoles', { backend: queryOptions.backend })];
+ const capabilities = paths ? await this.capabilities.fetch(paths) : {};
+ return capabilities[paths[0]];
},
async fetchCapabilitiesConnection(queryOptions) {
- return this.store.queryRecord('capabilities', this.pathQuery(queryOptions.backend, 'config'));
+ const paths = [this.capabilities.pathFor('databaseConfig', { backend: queryOptions.backend })];
+ const capabilities = paths ? await this.capabilities.fetch(paths) : {};
+ return capabilities[paths[0]];
},
- model() {
+ async model() {
const backend = getEnginePathParam(this);
const queryOptions = { backend, id: '' };
- const connection = this.fetchConnection(queryOptions);
- const role = this.fetchAllRoles(queryOptions);
- const roleCapabilities = this.fetchCapabilitiesRole(queryOptions);
- const staticRoleCapabilities = this.fetchCapabilitiesStaticRole(queryOptions);
- const connectionCapabilities = this.fetchCapabilitiesConnection(queryOptions);
+ const connection = await this.fetchConnection(queryOptions);
+ const role = await this.fetchAllRoles(queryOptions);
+ const roleCapabilities = await this.fetchCapabilitiesRole(queryOptions);
+ const staticRoleCapabilities = await this.fetchCapabilitiesStaticRole(queryOptions);
+ const connectionCapabilities = await this.fetchCapabilitiesConnection(queryOptions);
return hash({
backend,
@@ -71,7 +113,7 @@ export default Route.extend({
setupController(controller, model) {
this._super(...arguments);
- const showEmptyState = model.connections === 404 && model.roles === 404;
+ const showEmptyState = model.connections === 404 && (model.roles === undefined || model.roles === 404);
const noConnectionCapabilities =
!model.connectionCapabilities.canList &&
!model.connectionCapabilities.canCreate &&
diff --git a/ui/app/templates/vault/cluster/secrets/backend/credentials.hbs b/ui/app/templates/vault/cluster/secrets/backend/credentials.hbs
index e072608729..a8249b31c3 100644
--- a/ui/app/templates/vault/cluster/secrets/backend/credentials.hbs
+++ b/ui/app/templates/vault/cluster/secrets/backend/credentials.hbs
@@ -7,7 +7,7 @@
{{else if (eq this.model.backendType "totp")}}
diff --git a/ui/tests/acceptance/secrets/backend/database/secret-test.js b/ui/tests/acceptance/secrets/backend/database/secret-test.js
index dba92d0843..36e20a6e44 100644
--- a/ui/tests/acceptance/secrets/backend/database/secret-test.js
+++ b/ui/tests/acceptance/secrets/backend/database/secret-test.js
@@ -644,10 +644,7 @@ module('Acceptance | secrets/database/*', function (hooks) {
assert
.dom('[data-test-secret-list-tab="Roles"]')
.doesNotExist(`does not show the roles tab because it does not have permissions`);
- assert
- .dom('[data-test-overview-card="Connections"]')
- .exists({ count: 1 }, 'renders only the connection card');
- await click('[data-test-action-text="Configure new"]');
+ await click(SES.createSecretLink);
assert.strictEqual(currentURL(), `/vault/secrets-engines/${backend}/create?itemType=connection`);
});
});
diff --git a/ui/tests/acceptance/secrets/backend/database/workflow-test.js b/ui/tests/acceptance/secrets/backend/database/workflow-test.js
index 0997e9d20e..4f521eccc9 100644
--- a/ui/tests/acceptance/secrets/backend/database/workflow-test.js
+++ b/ui/tests/acceptance/secrets/backend/database/workflow-test.js
@@ -335,7 +335,7 @@ module('Acceptance | database workflow', function (hooks) {
.hasText('generated-password', 'Password is generated');
assert
.dom(GENERAL.infoRowValue('Lease Duration'))
- .hasText('3600', 'shows lease duration from response');
+ .hasText('1 hour', 'shows lease duration from response');
assert
.dom(GENERAL.infoRowValue('Lease ID'))
.hasText(`database/creds/${roleName}/abcd`, 'shows lease ID from response');