diff --git a/changelog/_11798.txt b/changelog/_11798.txt
index 93a22bbe39..98ad728407 100644
--- a/changelog/_11798.txt
+++ b/changelog/_11798.txt
@@ -1,3 +1,3 @@
```release-note:feature
-**UI Policy Generator (Enterprise)**: Adds policy generator flyout to KV V2 secrets engine prepopulated with relevant API requests for each page.
+**UI Policy Generator (Enterprise)**: Adds policy generator flyout to KV V2 and PKI secrets engines prepopulated with relevant API requests for each page.
```
\ No newline at end of file
diff --git a/ui/app/utils/constants/capabilities.ts b/ui/app/utils/constants/capabilities.ts
index d17d38d834..01a8aed799 100644
--- a/ui/app/utils/constants/capabilities.ts
+++ b/ui/app/utils/constants/capabilities.ts
@@ -16,51 +16,56 @@ export const SUDO_PATHS = [
export const SUDO_PATH_PREFIXES = ['sys/leases/revoke-prefix', 'sys/leases/revoke-force'];
export const PATH_MAP = {
- customLogin: apiPath`sys/config/ui/login/default-auth/${'id'}`,
- customMessages: apiPath`sys/config/ui/custom-messages/${'id'}`,
- syncActivate: apiPath`sys/activation-flags/secrets-sync/activate`,
- syncDestination: apiPath`sys/sync/destinations/${'type'}/${'name'}`,
- syncSetAssociation: apiPath`sys/sync/destinations/${'type'}/${'name'}/associations/set`,
- syncRemoveAssociation: apiPath`sys/sync/destinations/${'type'}/${'name'}/associations/remove`,
- kvConfig: apiPath`${'path'}/config`,
- kvMetadata: apiPath`${'backend'}/metadata/${'path'}`,
authMethodConfig: apiPath`auth/${'path'}/config`,
authMethodConfigAws: apiPath`auth/${'path'}/config/client`,
authMethodDelete: apiPath`sys/auth/${'path'}`,
- pkiRevoke: apiPath`${'backend'}/revoke`,
+ clientsActivityExport: apiPath`${'namespace'}/sys/internal/counters/activity/export`,
+ clientsConfig: apiPath`sys/internal/counters/config`,
+ customLogin: apiPath`sys/config/ui/login/default-auth/${'id'}`,
+ customMessages: apiPath`sys/config/ui/custom-messages/${'id'}`,
+ kmipCredentialsRevoke: apiPath`${'backend'}/scope/${'scope'}/role/${'role'}/credentials/revoke`,
+ kmipRole: apiPath`${'backend'}/scopes/${'scope'}/roles/${'name'}`,
+ kmipScope: apiPath`${'backend'}/scopes/${'name'}`,
+ kubernetesCreds: apiPath`${'backend'}/creds/${'name'}`,
+ kubernetesRole: apiPath`${'backend'}/role/${'name'}`,
+ kvConfig: apiPath`${'path'}/config`,
+ kvMetadata: apiPath`${'backend'}/metadata/${'path'}`,
+ ldapDynamicRole: apiPath`${'backend'}/role/${'name'}`,
+ ldapDynamicRoleCreds: apiPath`${'backend'}/creds/${'name'}`,
+ ldapLibrary: apiPath`${'backend'}/library/${'name'}`,
+ ldapLibraryCheckIn: apiPath`${'backend'}/library/${'name'}/check-in`,
+ ldapLibraryCheckOut: apiPath`${'backend'}/library/${'name'}/check-out`,
+ ldapRotateStaticRole: apiPath`${'backend'}/rotate-role/${'name'}`,
+ ldapStaticRole: apiPath`${'backend'}/static-role/${'name'}`,
+ ldapStaticRoleCreds: apiPath`${'backend'}/static-cred/${'name'}`,
+ pkiCertificates: apiPath`${'backend'}/certificates`,
pkiConfigAcme: apiPath`${'backend'}/config/acme`,
+ pkiConfigAutoTidy: apiPath`${'backend'}/config/auto-tidy`,
pkiConfigCluster: apiPath`${'backend'}/config/cluster`,
pkiConfigCrl: apiPath`${'backend'}/config/crl`,
pkiConfigUrls: apiPath`${'backend'}/config/urls`,
- pkiIssuersImportBundle: apiPath`${'backend'}/issuers/import/bundle`,
- pkiIssuersGenerateRoot: apiPath`${'backend'}/issuers/generate/root/${'type'}`,
- pkiIssuersGenerateIntermediate: apiPath`${'backend'}/issuers/generate/intermediate/${'type'}`,
- pkiIssuersCrossSign: apiPath`${'backend'}/issuers/cross-sign`,
- pkiIssuer: apiPath`${'backend'}/issuer/${'issuerId'}`,
- pkiIssuerSignIntermediate: apiPath`${'backend'}/issuer/${'issuerId'}/sign-intermediate`,
- pkiRoot: apiPath`${'backend'}/root`,
- pkiRootRotate: apiPath`${'backend'}/root/rotate/${'type'}`,
pkiIntermediateCrossSign: apiPath`${'backend'}/intermediate/cross-sign`,
+ pkiIssue: apiPath`${'backend'}/issue/${'id'}`,
+ pkiIssuer: apiPath`${'backend'}/issuer/${'issuerId'}`,
+ pkiIssuersCrossSign: apiPath`${'backend'}/issuers/cross-sign`,
+ pkiIssuersGenerateIntermediate: apiPath`${'backend'}/issuers/generate/intermediate/${'type'}`,
+ pkiIssuersGenerateRoot: apiPath`${'backend'}/issuers/generate/root/${'type'}`,
+ pkiIssuerSignIntermediate: apiPath`${'backend'}/issuer/${'issuerId'}/sign-intermediate`,
+ pkiIssuersImportBundle: apiPath`${'backend'}/issuers/import/bundle`,
pkiKey: apiPath`${'backend'}/key/${'keyId'}`,
pkiKeysGenerate: apiPath`${'backend'}/keys/generate`,
pkiKeysImport: apiPath`${'backend'}/keys/import`,
+ pkiRevoke: apiPath`${'backend'}/revoke`,
pkiRole: apiPath`${'backend'}/roles/${'id'}`,
- pkiIssue: apiPath`${'backend'}/issue/${'id'}`,
+ pkiRoles: apiPath`${'backend'}/roles`,
+ pkiRoot: apiPath`${'backend'}/root`,
+ pkiRootRotate: apiPath`${'backend'}/root/rotate/${'type'}`,
pkiSign: apiPath`${'backend'}/sign/${'id'}`,
pkiSignVerbatim: apiPath`${'backend'}/sign-verbatim/${'id'}`,
- ldapStaticRole: apiPath`${'backend'}/static-role/${'name'}`,
- ldapDynamicRole: apiPath`${'backend'}/role/${'name'}`,
- ldapRotateStaticRole: apiPath`${'backend'}/rotate-role/${'name'}`,
- ldapStaticRoleCreds: apiPath`${'backend'}/static-cred/${'name'}`,
- ldapDynamicRoleCreds: apiPath`${'backend'}/creds/${'name'}`,
- ldapLibrary: apiPath`${'backend'}/library/${'name'}`,
- ldapLibraryCheckOut: apiPath`${'backend'}/library/${'name'}/check-out`,
- ldapLibraryCheckIn: apiPath`${'backend'}/library/${'name'}/check-in`,
- kubernetesRole: apiPath`${'backend'}/role/${'name'}`,
- kubernetesCreds: apiPath`${'backend'}/creds/${'name'}`,
- kmipScope: apiPath`${'backend'}/scopes/${'name'}`,
- kmipRole: apiPath`${'backend'}/scopes/${'scope'}/roles/${'name'}`,
- kmipCredentialsRevoke: apiPath`${'backend'}/scope/${'scope'}/role/${'role'}/credentials/revoke`,
- clientsConfig: apiPath`sys/internal/counters/config`,
- clientsActivityExport: apiPath`${'namespace'}/sys/internal/counters/activity/export`,
+ pkiTidy: apiPath`${'backend'}/tidy`,
+ pkiTidyStatus: apiPath`${'backend'}/tidy/status`,
+ syncActivate: apiPath`sys/activation-flags/secrets-sync/activate`,
+ syncDestination: apiPath`sys/sync/destinations/${'type'}/${'name'}`,
+ syncRemoveAssociation: apiPath`sys/sync/destinations/${'type'}/${'name'}/associations/remove`,
+ syncSetAssociation: apiPath`sys/sync/destinations/${'type'}/${'name'}/associations/set`,
};
diff --git a/ui/lib/core/addon/components/input-search.hbs b/ui/lib/core/addon/components/input-search.hbs
index da5e63120b..abf60f2e49 100644
--- a/ui/lib/core/addon/components/input-search.hbs
+++ b/ui/lib/core/addon/components/input-search.hbs
@@ -8,7 +8,7 @@
@type="text"
@id={{@id}}
@value={{this.searchInput}}
- {{on "keyup" this.inputChanged}}
+ {{on (or @changeEvent "keyup") this.inputChanged}}
placeholder={{@placeholder}}
autocomplete="off"
data-test-input-search={{@id}}
diff --git a/ui/lib/core/addon/components/input-search.js b/ui/lib/core/addon/components/input-search.js
index 2c4431ab9a..35a87c6006 100644
--- a/ui/lib/core/addon/components/input-search.js
+++ b/ui/lib/core/addon/components/input-search.js
@@ -9,13 +9,14 @@ import { tracked } from '@glimmer/tracking';
/**
* @module InputSearch
- * This component renders an input that fires a callback on "keyup" containing the input's value
+ * This component renders an input that fires a callback on "keyup" or the passed change event containing the input's value
*
* @example
*
*
* @param {string} [id] - unique id for the input
* @param {string} [initialValue] - initial search value, i.e. a secret path prefix, that pre-fills the input field
+ * @param {string} [changeEvent="keyup"] - the input change event for which to fire the onChange callback
* @param {string} [placeholder] - placeholder text for the input
* @param {string} [label] - label for the input
* @param {string} [subtext] - displays below the label
diff --git a/ui/lib/pki/addon/components/page/pki-key-list.hbs b/ui/lib/pki/addon/components/page/pki-key-list.hbs
index 0c65a866c8..4d5109a9a8 100644
--- a/ui/lib/pki/addon/components/page/pki-key-list.hbs
+++ b/ui/lib/pki/addon/components/page/pki-key-list.hbs
@@ -44,34 +44,36 @@
- {{#if (or @canRead @canEdit)}}
-
-
- {{#if @canRead}}
-
- Details
-
- {{/if}}
- {{#if @canEdit}}
-
- Edit
-
- {{/if}}
-
- {{/if}}
+ {{#let (get @keyPermsById pkiKey.key_id) as |perms|}}
+ {{#if (or perms.canRead perms.canUpdate)}}
+
+
+ {{#if perms.canRead}}
+
+ Details
+
+ {{/if}}
+ {{#if perms.canUpdate}}
+
+ Edit
+
+ {{/if}}
+
+ {{/if}}
+ {{/let}}
diff --git a/ui/lib/pki/addon/components/page/pki-overview.hbs b/ui/lib/pki/addon/components/page/pki-overview.hbs
index 7b1de45e82..428300ea93 100644
--- a/ui/lib/pki/addon/components/page/pki-overview.hbs
+++ b/ui/lib/pki/addon/components/page/pki-overview.hbs
@@ -19,11 +19,11 @@
<:content>
- {{format-number (if (eq @issuers 404) 0 @issuers.length)}}
+ {{format-number @issuers.length}}
- {{#if (not-eq @roles 403)}}
+ {{#if @canListRoles}}
<:content>
- {{format-number (if (eq @roles 404) 0 @roles.length)}}
+ {{format-number @roles.length}}
{{/if}}
-
+
<:content>
-
+ {{#if @canListRoles}}
+
+ {{else}}
+
+ {{/if}}
-
+
<:content>
-
+ {{#if @canListCertificates}}
+
+ {{else}}
+
+ {{/if}}
<:actions>
+
{{#if @configRoute}}
{{else}}
diff --git a/ui/lib/pki/addon/components/pki-page-header.ts b/ui/lib/pki/addon/components/pki-page-header.ts
index d55f06f5b4..4e6436c7fa 100644
--- a/ui/lib/pki/addon/components/pki-page-header.ts
+++ b/ui/lib/pki/addon/components/pki-page-header.ts
@@ -8,10 +8,12 @@ import { tracked } from '@glimmer/tracking';
import Component from '@glimmer/component';
import { task } from 'ember-concurrency';
-import type SecretsEngineResource from 'vault/resources/secrets/engine';
-import type RouterService from '@ember/routing/router-service';
-import type FlashMessageService from 'vault/services/flash-messages';
+import type { PATH_MAP } from 'vault/utils/constants/capabilities';
import type ApiService from 'vault/services/api';
+import type CapabilitiesService from 'vault/services/capabilities';
+import type FlashMessageService from 'vault/services/flash-messages';
+import type RouterService from '@ember/routing/router-service';
+import type SecretsEngineResource from 'vault/resources/secrets/engine';
/**
* @module PkiPageHeader
@@ -26,22 +28,40 @@ interface Args {
backend: { id: string };
}
+const ROUTE_PATH_MAP = {
+ 'vault.cluster.secrets.backend.pki.certificates.index': ['pkiCertificates'],
+ 'vault.cluster.secrets.backend.pki.roles.index': ['pkiRoles'],
+ 'vault.cluster.secrets.backend.pki.tidy.index': ['pkiTidy', 'pkiTidyStatus', 'pkiConfigAutoTidy'],
+} satisfies Record;
+
export default class PkiPageHeader extends Component {
@service('app-router') declare readonly router: RouterService;
@service declare readonly api: ApiService;
@service declare readonly flashMessages: FlashMessageService;
+ @service declare readonly capabilities: CapabilitiesService;
@tracked engineToDisable = undefined;
+
get breadcrumbs() {
return [
{ label: 'Vault', route: 'vault', icon: 'vault', linkExternal: true },
{ label: 'Secrets engines', route: 'secrets', linkExternal: true },
- {
- label: this.args?.backend?.id,
- },
+ { label: this.args?.backend?.id },
];
}
+ // PKI does not make capability requests for these routes
+ // so manually pass the relevant paths for each route.
+ get policyPaths() {
+ const backend = this.args?.backend?.id;
+ const { currentRouteName } = this.router;
+ const paths = ROUTE_PATH_MAP[currentRouteName as keyof typeof ROUTE_PATH_MAP];
+ if (paths) {
+ return this.capabilities.pathsForList(paths, { backend });
+ }
+ return null;
+ }
+
@task
*disableEngine(engine: SecretsEngineResource) {
const { engineType, id, path } = engine;
diff --git a/ui/lib/pki/addon/routes/keys/index.js b/ui/lib/pki/addon/routes/keys/index.js
index 6dca2961b6..b690ac86bd 100644
--- a/ui/lib/pki/addon/routes/keys/index.js
+++ b/ui/lib/pki/addon/routes/keys/index.js
@@ -22,21 +22,24 @@ export default class PkiKeysIndexRoute extends Route {
},
};
- async fetchCapabilities(keyId) {
+ async fetchCapabilities(keys) {
const { pathFor } = this.capabilities;
const backend = this.secretMountPath.currentPath;
+ const keyPathsById = this.keyPathsById(backend, keys);
const pathMap = {
import: pathFor('pkiKeysImport', { backend }),
generate: pathFor('pkiKeysGenerate', { backend }),
- key: pathFor('pkiKey', { backend, keyId }),
+ ...keyPathsById,
};
- const perms = await this.capabilities.fetch(Object.values(pathMap));
+ const apiPaths = Object.values(pathMap);
+ const perms = await this.capabilities.fetch(apiPaths, {
+ routeForCache: 'vault.cluster.secrets.backend.pki.keys',
+ });
return {
canImportKeys: perms[pathMap.import].canUpdate,
canGenerateKeys: perms[pathMap.generate].canUpdate,
- canRead: perms[pathMap.key].canRead,
- canEdit: perms[pathMap.key].canUpdate,
+ keyPermsById: this.keyCapabilitiesById(keyPathsById, perms),
};
}
@@ -53,7 +56,7 @@ export default class PkiKeysIndexRoute extends Route {
PkiListKeysListEnum.TRUE
);
const keys = this.api.keyInfoToArray(response, 'key_id');
- const capabilities = await this.fetchCapabilities(keys[0].key_id);
+ const capabilities = await this.fetchCapabilities(keys);
Object.assign(model, { ...capabilities, keys: paginate(keys, { page }) });
} catch (e) {
if (e.response.status === 404) {
@@ -82,4 +85,20 @@ export default class PkiKeysIndexRoute extends Route {
controller.set('page', undefined);
}
}
+
+ keyPathsById(backend, keys) {
+ // Construct API path for each key in the list
+ return Object.fromEntries(
+ keys.map(({ key_id: keyId }) => [keyId, this.capabilities.pathFor('pkiKey', { backend, keyId })])
+ );
+ }
+
+ keyCapabilitiesById(keyPathsById, perms) {
+ // Iterate over key ids and return an object with Capabilities as their value
+ return Object.fromEntries(
+ Object.entries(keyPathsById)
+ .filter(([, apiPath]) => apiPath in perms)
+ .map(([keyId, apiPath]) => [keyId, perms[apiPath]])
+ );
+ }
}
diff --git a/ui/lib/pki/addon/routes/overview.js b/ui/lib/pki/addon/routes/overview.js
index 2b9e8f1e4d..f0ddef9316 100644
--- a/ui/lib/pki/addon/routes/overview.js
+++ b/ui/lib/pki/addon/routes/overview.js
@@ -24,9 +24,9 @@ export const getCliMessage = (msg) => {
@withConfig()
export default class PkiOverviewRoute extends Route {
- @service secretMountPath;
- @service auth;
@service api;
+ @service capabilities;
+ @service secretMountPath;
async fetchAllCertificates() {
try {
@@ -36,7 +36,10 @@ export default class PkiOverviewRoute extends Route {
);
return keys;
} catch (e) {
- return e.response.status;
+ const { status } = await this.api.parseError(e);
+ // If there was a permissions (403) or some other error
+ // swallow because this data is for rendering overview cards
+ return status === 404 ? [] : null;
}
}
@@ -48,7 +51,10 @@ export default class PkiOverviewRoute extends Route {
);
return keys;
} catch (e) {
- return e.response.status;
+ const { status } = await this.api.parseError(e);
+ // If there was a permissions (403) or some other error
+ // swallow because this data is for rendering overview cards
+ return status === 404 ? [] : null;
}
}
@@ -60,17 +66,39 @@ export default class PkiOverviewRoute extends Route {
);
return keys;
} catch (e) {
- return e.response.status;
+ const { status } = await this.api.parseError(e);
+ return status === 404 ? [] : null;
}
}
+ async fetchCapabilities() {
+ const { pathFor } = this.capabilities;
+ const backend = this.secretMountPath.currentPath;
+ // the issuers list endpoint is unauthenticated so we do not need to check capabilities for it
+ const pathMap = {
+ certificates: pathFor('pkiCertificates', { backend }),
+ roles: pathFor('pkiRoles', { backend }),
+ };
+ const apiPaths = Object.values(pathMap);
+ const perms = await this.capabilities.fetch(apiPaths, {
+ routeForCache: 'vault.cluster.secrets.backend.pki.overview',
+ });
+ return {
+ canListCertificates: perms[pathMap.certificates].canList,
+ canListRoles: perms[pathMap.roles].canList,
+ };
+ }
+
async model() {
+ const { canListCertificates, canListRoles } = await this.fetchCapabilities();
return hash({
hasConfig: this.pkiMountHasConfig,
engine: this.modelFor('application'),
- roles: this.fetchAllRoles(),
+ roles: canListRoles ? this.fetchAllRoles() : null,
issuers: this.fetchAllIssuers(),
- certificates: this.fetchAllCertificates(),
+ certificates: canListCertificates ? this.fetchAllCertificates() : null,
+ canListCertificates,
+ canListRoles,
});
}
diff --git a/ui/lib/pki/addon/templates/keys/index.hbs b/ui/lib/pki/addon/templates/keys/index.hbs
index d0bbbea5f4..4c91a19601 100644
--- a/ui/lib/pki/addon/templates/keys/index.hbs
+++ b/ui/lib/pki/addon/templates/keys/index.hbs
@@ -11,7 +11,6 @@
@backend={{this.model.parentModel.id}}
@canImportKeys={{this.model.canImportKeys}}
@canGenerateKeys={{this.model.canGenerateKeys}}
- @canRead={{this.model.canRead}}
- @canEdit={{this.model.canEdit}}
+ @keyPermsById={{this.model.keyPermsById}}
@hasConfig={{this.model.hasConfig}}
/>
\ No newline at end of file
diff --git a/ui/lib/pki/addon/templates/overview.hbs b/ui/lib/pki/addon/templates/overview.hbs
index 4cd22aeb21..9f85b82009 100644
--- a/ui/lib/pki/addon/templates/overview.hbs
+++ b/ui/lib/pki/addon/templates/overview.hbs
@@ -13,6 +13,8 @@
@roles={{this.model.roles}}
@certificates={{this.model.certificates}}
@engine={{this.model.engine}}
+ @canListCertificates={{this.model.canListCertificates}}
+ @canListRoles={{this.model.canListRoles}}
/>
{{else}}
diff --git a/ui/tests/acceptance/pki/pki-overview-test.js b/ui/tests/acceptance/pki/pki-overview-test.js
index be103b3749..459acfa062 100644
--- a/ui/tests/acceptance/pki/pki-overview-test.js
+++ b/ui/tests/acceptance/pki/pki-overview-test.js
@@ -80,10 +80,11 @@ module('Acceptance | pki overview', function (hooks) {
assert.dom(`${overviewCard.container('Roles')} p`).hasText('1');
});
- test('hides roles card if user does not have permissions', async function (assert) {
+ test('hides roles and certificates card if user does not have permissions', async function (assert) {
await login(this.pkiIssuersList);
await visit(`/vault/secrets-engines/${this.mountPath}/pki/overview`);
assert.dom(overviewCard.title('Roles')).doesNotExist('Roles card does not exist');
+ assert.dom(overviewCard.title('Certificates')).doesNotExist('Certificates card does not exist');
assert.dom(overviewCard.title('Issuers')).hasText('Issuers');
});
diff --git a/ui/tests/integration/components/pki/page/pki-key-list-test.js b/ui/tests/integration/components/pki/page/pki-key-list-test.js
index b66a63f401..bfa3e91325 100644
--- a/ui/tests/integration/components/pki/page/pki-key-list-test.js
+++ b/ui/tests/integration/components/pki/page/pki-key-list-test.js
@@ -34,8 +34,26 @@ module('Integration | Component | pki key list page', function (hooks) {
this.keys.meta = STANDARD_META;
this.canImportKeys = true;
this.canGenerateKeys = true;
- this.canRead = true;
- this.canEdit = true;
+ this.keyPermsById = {
+ '724862ff-6438-bad0-b598-77a6c7f4e934': {
+ canCreate: true,
+ canDelete: true,
+ canList: true,
+ canPatch: true,
+ canRead: true,
+ canSudo: true,
+ canUpdate: true,
+ },
+ '9fdddf12-9ce3-0268-6b34-dc1553b00175': {
+ canCreate: true,
+ canDelete: true,
+ canList: true,
+ canPatch: true,
+ canRead: true,
+ canSudo: true,
+ canUpdate: true,
+ },
+ };
this.renderComponent = () =>
render(
@@ -45,8 +63,7 @@ module('Integration | Component | pki key list page', function (hooks) {
@mountPoint="vault.cluster.secrets.backend.pki"
@canImportKeys={{this.canImportKeys}}
@canGenerateKeys={{this.canGenerateKeys}}
- @canRead={{this.canRead}}
- @canEdit={{this.canEdit}}
+ @keyPermsById={{this.keyPermsById}}
/>,
`,
{ owner: this.engine }
@@ -92,9 +109,10 @@ module('Integration | Component | pki key list page', function (hooks) {
this.canImportKeys = false;
this.canGenerateKeys = false;
- this.canRead = false;
- this.canEdit = false;
-
+ this.keyPermsById = {
+ '724862ff-6438-bad0-b598-77a6c7f4e934': { canRead: false, canUpdate: false },
+ '9fdddf12-9ce3-0268-6b34-dc1553b00175': { canRead: false, canUpdate: false },
+ };
await this.renderComponent();
assert.dom(PKI_KEYS.importKey).doesNotExist('renders import action');
diff --git a/ui/tests/integration/components/pki/page/pki-overview-test.js b/ui/tests/integration/components/pki/page/pki-overview-test.js
index cc63ea979b..46896fd6b4 100644
--- a/ui/tests/integration/components/pki/page/pki-overview-test.js
+++ b/ui/tests/integration/components/pki/page/pki-overview-test.js
@@ -24,10 +24,19 @@ module('Integration | Component | Page::PkiOverview', function (hooks) {
this.roles = ['role-0', 'role-1', 'role-2'];
this.certificates = ['22:2222:22222:2222', '33:3333:33333:3333'];
this.engineId = 'pki';
+ this.canListCertificates = true;
+ this.canListRoles = true;
this.renderComponent = () =>
render(
- hbs``,
+ hbs``,
{ owner: this.engine }
);
});
@@ -39,6 +48,14 @@ module('Integration | Component | Page::PkiOverview', function (hooks) {
.hasText(
'Issuers View issuers The total number of issuers in this PKI mount. Includes both root and intermediate certificates. 2'
);
+
+ this.issuers = [];
+ await this.renderComponent();
+ assert
+ .dom(overviewCard.container('Issuers'))
+ .hasText(
+ 'Issuers View issuers The total number of issuers in this PKI mount. Includes both root and intermediate certificates. 0'
+ );
});
test('shows the correct information on roles card', async function (assert) {
@@ -48,7 +65,7 @@ module('Integration | Component | Page::PkiOverview', function (hooks) {
.hasText(
'Roles View roles The total number of roles in this PKI mount that have been created to generate certificates. 3'
);
- this.roles = 404;
+ this.roles = [];
await this.renderComponent();
assert
.dom(overviewCard.container('Roles'))
@@ -57,17 +74,42 @@ module('Integration | Component | Page::PkiOverview', function (hooks) {
);
});
- test('shows the input search fields for View Certificates card', async function (assert) {
+ test('shows the search select dropdown for View Certificates card', async function (assert) {
+ await this.renderComponent();
+ assert.dom(overviewCard.title('View certificate')).hasText('View certificate');
+ assert
+ .dom(overviewCard.description('View certificate'))
+ .hasText('Quickly view a certificate by looking up its serial number.');
+ assert.dom(PKI_OVERVIEW.viewCertificateInput).exists();
+ assert.dom(GENERAL.inputSearch('certificate')).doesNotExist('it does not render certificate input');
+ assert.dom(PKI_OVERVIEW.viewCertificateButton).hasText('View');
+ });
+
+ test('shows the search select dropdown for Issue Certificates card', async function (assert) {
await this.renderComponent();
assert.dom(overviewCard.title('Issue certificate')).hasText('Issue certificate');
+ assert
+ .dom(overviewCard.description('Issue certificate'))
+ .hasText('Begin issuing a certificate by choosing a role.');
assert.dom(PKI_OVERVIEW.issueCertificateInput).exists();
+ assert.dom(GENERAL.inputSearch('role')).doesNotExist('it does not render role input');
assert.dom(PKI_OVERVIEW.issueCertificateButton).hasText('Issue');
});
- test('shows the input search fields for Issue Certificates card', async function (assert) {
+ test('it renders manual search inputs when no list permission', async function (assert) {
+ this.canListCertificates = false;
+ this.canListRoles = false;
await this.renderComponent();
- assert.dom(overviewCard.title('View certificate')).hasText('View certificate');
- assert.dom(PKI_OVERVIEW.viewCertificateInput).exists();
- assert.dom(PKI_OVERVIEW.viewCertificateButton).hasText('View');
+ assert.dom(overviewCard.container('Roles')).doesNotExist();
+ assert
+ .dom(overviewCard.description('View certificate'))
+ .hasText('Quickly view a certificate by providing its serial number.');
+ assert
+ .dom(overviewCard.description('Issue certificate'))
+ .hasText('Begin issuing a certificate by entering a role.');
+ assert.dom(PKI_OVERVIEW.issueCertificateInput).doesNotExist('role search select does not render');
+ assert.dom(GENERAL.inputSearch('role')).exists('it renders input instead of search select');
+ assert.dom(PKI_OVERVIEW.viewCertificateInput).doesNotExist('certificate search select does not render');
+ assert.dom(GENERAL.inputSearch('certificate')).exists('it renders input instead of search selects');
});
});