Skip to content

Commit

Permalink
Backport UI: Add pagination to new PKI (#23238)
Browse files Browse the repository at this point in the history
* UI: Show unsupported screen if replication unsupported (#23178)

* UI: add pagination to new PKI (#23193)
  • Loading branch information
hashishaw authored Sep 22, 2023
1 parent 5603003 commit d3927bc
Show file tree
Hide file tree
Showing 29 changed files with 653 additions and 271 deletions.
3 changes: 3 additions & 0 deletions changelog/23193.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: Add pagination to PKI roles, keys, issuers, and certificates list pages
```
5 changes: 4 additions & 1 deletion ui/app/services/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ export default class StoreService extends Store {
// pageFilter: a string that will be used to do a fuzzy match against the
// results, this is done pre-pagination
lazyPaginatedQuery(modelType, query, adapterOptions) {
const skipCache = query.skipCache;
// We don't want skipCache to be part of the actual query key, so remove it
delete query.skipCache;
const adapter = this.adapterFor(modelType);
const modelName = normalizeModelName(modelType);
const dataCache = this.getDataset(modelName, query);
const dataCache = skipCache ? this.clearDataset(modelName) : this.getDataset(modelName, query);
const responsePath = query.responsePath;
assert('responsePath is required', responsePath);
assert('page is required', typeof query.page === 'number');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { tracked } from '@glimmer/tracking';
import errorMessage from 'vault/utils/error-message';
import type RouterService from '@ember/routing/router-service';
import type FlashMessageService from 'vault/services/flash-messages';
import type Store from '@ember-data/store';
import type Store from 'vault/services/store';
import type VersionService from 'vault/services/version';

interface Args {
Expand Down
170 changes: 108 additions & 62 deletions ui/lib/pki/addon/components/page/pki-issuer-list.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,114 @@
SPDX-License-Identifier: BUSL-1.1
~}}

{{#each @issuers as |pkiIssuer idx|}}
<LinkedBlock class="list-item-row" @params={{array "issuers.issuer.details" pkiIssuer.id}} @linkPrefix={{@mountPoint}}>
<div class="level is-mobile">
<div class="level-left">
<div data-test-issuer-list={{pkiIssuer.id}}>
<Icon @name="certificate" class="has-text-grey-light" />
<span class="has-text-weight-semibold is-underline">
{{pkiIssuer.issuerRef}}
{{#if pkiIssuer.issuerName}}
<span class="tag has-text-grey-dark">{{pkiIssuer.id}}</span>
{{/if}}
</span>
<div class="is-flex-row has-left-margin-l has-top-margin-xs">
{{#if pkiIssuer.isDefault}}
<span class="tag has-text-grey-dark" data-test-is-default={{idx}}>default issuer</span>
{{/if}}
{{#if (not-eq pkiIssuer.isRoot undefined)}}
<span class="tag has-text-grey-dark" data-test-is-root-tag={{idx}}>{{if
pkiIssuer.isRoot
"root"
"intermediate"
}}</span>
{{/if}}
{{#if pkiIssuer.serialNumber}}
<span class="tag is-transparent has-right-margin-none" data-test-serial-number={{idx}}>
<InfoTooltip>
Serial number
</InfoTooltip>
{{pkiIssuer.serialNumber}}
<PkiPaginatedList @listRoute="issuers.index" @list={{@issuers}}>
<:actions>
<ToolbarLink @route="issuers.import" data-test-generate-issuer="import">
Import
</ToolbarLink>
<BasicDropdown @class="popup-menu" @horizontalPosition="auto-right" @verticalPosition="below" as |D|>
<D.Trigger
class={{concat "toolbar-link" (if D.isOpen " is-active")}}
@htmlTag="button"
data-test-issuer-generate-dropdown
>
Generate
<Chevron @direction="down" @isButton={{true}} />
</D.Trigger>
<D.Content @defaultClass="popup-menu-content">
<nav class="box menu" aria-label="generate options">
<ul class="menu-list">
<li class="action">
<LinkTo @route="issuers.generate-root" {{on "click" (fn this.onLinkClick D)}} data-test-generate-issuer="root">
Root
</LinkTo>
</li>
<li class="action">
<LinkTo
@route="issuers.generate-intermediate"
{{on "click" (fn this.onLinkClick D)}}
data-test-generate-issuer="intermediate"
>
Intermediate CSR
</LinkTo>
</li>
</ul>
</nav>
</D.Content>
</BasicDropdown>
</:actions>
<:list as |issuers|>
{{#each issuers as |pkiIssuer idx|}}
<LinkedBlock class="list-item-row" @params={{array "issuers.issuer.details" pkiIssuer.id}} @linkPrefix={{@mountPoint}}>
<div class="level is-mobile">
<div class="level-left">
<div data-test-issuer-list={{pkiIssuer.id}}>
<Icon @name="certificate" class="has-text-grey-light" />
<span class="has-text-weight-semibold is-underline">
{{pkiIssuer.issuerRef}}
{{#if pkiIssuer.issuerName}}
<span class="tag has-text-grey-dark">{{pkiIssuer.id}}</span>
{{/if}}
</span>
{{/if}}
{{#if pkiIssuer.parsedCertificate.common_name}}
<span class="tag is-transparent has-left-margin-none" data-test-common-name={{idx}}>
<InfoTooltip>
Common name
</InfoTooltip>
{{pkiIssuer.parsedCertificate.common_name}}
</span>
{{/if}}
<div class="is-flex-row has-left-margin-l has-top-margin-xs">
{{#if pkiIssuer.isDefault}}
<span class="tag has-text-grey-dark" data-test-is-default={{idx}}>default issuer</span>
{{/if}}
{{#if (not-eq pkiIssuer.isRoot undefined)}}
<span class="tag has-text-grey-dark" data-test-is-root-tag={{idx}}>{{if
pkiIssuer.isRoot
"root"
"intermediate"
}}</span>
{{/if}}
{{#if pkiIssuer.serialNumber}}
<span class="tag is-transparent has-right-margin-none" data-test-serial-number={{idx}}>
<InfoTooltip>
Serial number
</InfoTooltip>
{{pkiIssuer.serialNumber}}
</span>
{{/if}}
{{#if pkiIssuer.parsedCertificate.common_name}}
<span class="tag is-transparent has-left-margin-none" data-test-common-name={{idx}}>
<InfoTooltip>
Common name
</InfoTooltip>
{{pkiIssuer.parsedCertificate.common_name}}
</span>
{{/if}}
</div>
</div>
</div>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
<PopupMenu>
<nav class="menu" aria-label="issuer config options">
<ul class="menu-list">
<li data-test-popup-menu-details>
<LinkTo @route="issuers.issuer.details" @model={{pkiIssuer.id}}>
Details
</LinkTo>
</li>
<li>
<LinkTo @route="issuers.issuer.edit" @model={{pkiIssuer.id}}>
Edit
</LinkTo>
</li>
</ul>
</nav>
</PopupMenu>
</div>
</div>
</div>
</div>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
<PopupMenu>
<nav class="menu" aria-label="issuer config options">
<ul class="menu-list">
<li data-test-popup-menu-details>
<LinkTo @route="issuers.issuer.details" @model={{pkiIssuer.id}}>
Details
</LinkTo>
</li>
<li>
<LinkTo @route="issuers.issuer.edit" @model={{pkiIssuer.id}}>
Edit
</LinkTo>
</li>
</ul>
</nav>
</PopupMenu>
</div>
</div>
</div>
</LinkedBlock>
{{/each}}
</LinkedBlock>
{{/each}}
</:list>
<:empty>
<EmptyState @title="PKI not configured" @message={{this.notConfiguredMessage}}>
<LinkTo @route="configuration.create">
Configure PKI
</LinkTo>
</EmptyState>
</:empty>
</PkiPaginatedList>
29 changes: 29 additions & 0 deletions ui/lib/pki/addon/components/page/pki-issuer-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { action } from '@ember/object';
import { next } from '@ember/runloop';
import Component from '@glimmer/component';
import { PKI_DEFAULT_EMPTY_STATE_MSG } from 'pki/routes/overview';
import type PkiIssuerModel from 'vault/models/pki/issuer';

interface BasicDropdown {
actions: {
close: CallableFunction;
};
}
interface Args {
issuers: PkiIssuerModel[];
mountPoint: string;
}

export default class PkiIssuerList extends Component<Args> {
notConfiguredMessage = PKI_DEFAULT_EMPTY_STATE_MSG;

// To prevent production build bug of passing D.actions to on "click": https://github.com/hashicorp/vault/pull/16983
@action onLinkClick(D: BasicDropdown) {
next(() => D.actions.close());
}
}
125 changes: 68 additions & 57 deletions ui/lib/pki/addon/components/page/pki-key-list.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
SPDX-License-Identifier: BUSL-1.1
~}}

<Toolbar>
<ToolbarActions>
<PkiPaginatedList @listRoute="keys.index" @list={{@keyModels}} @hasConfig={{@hasConfig}}>
<:actions>
{{#if @canImportKey}}
<ToolbarLink @route="keys.import" @type="download" data-test-pki-key-import>
Import
Expand All @@ -15,62 +15,73 @@
Generate
</ToolbarLink>
{{/if}}
</ToolbarActions>
</Toolbar>
<p class="has-padding">Below is information about the private keys used by the issuers to sign certificates. While
certificates represent a public assertion of an identity, private keys represent the private part of that identity, a
secret used to prove who they are and who they trust.</p>

{{#if @keyModels.length}}
{{#each @keyModels as |pkiKey|}}
<LinkedBlock class="list-item-row" @params={{array "keys.key.details" pkiKey.keyId}} @linkPrefix={{@mountPoint}}>
<div class="level is-mobile">
<div class="level-left">
<div>
<Icon @name="certificate" class="has-text-grey-light" />
<span class="has-text-weight-semibold is-underline" data-test-key={{if pkiKey.keyName "name" "id"}}>
{{or pkiKey.keyName pkiKey.id}}
</span>
<div class="is-flex-row has-left-margin-l has-top-margin-xs">
{{#if pkiKey.keyName}}
<span class="tag has-text-grey-dark" data-test-key="id">{{pkiKey.id}}</span>
{{/if}}
</:actions>
<:description>
<p class="has-padding">Below is information about the private keys used by the issuers to sign certificates. While
certificates represent a public assertion of an identity, private keys represent the private part of that identity, a
secret used to prove who they are and who they trust.</p>
</:description>
<:list as |keys|>
{{#each keys as |pkiKey|}}
<LinkedBlock class="list-item-row" @params={{array "keys.key.details" pkiKey.keyId}} @linkPrefix={{@mountPoint}}>
<div class="level is-mobile">
<div class="level-left">
<div>
<Icon @name="certificate" class="has-text-grey-light" />
<span class="has-text-weight-semibold is-underline" data-test-key={{if pkiKey.keyName "name" "id"}}>
{{or pkiKey.keyName pkiKey.id}}
</span>
<div class="is-flex-row has-left-margin-l has-top-margin-xs">
{{#if pkiKey.keyName}}
<span class="tag has-text-grey-dark" data-test-key="id">{{pkiKey.id}}</span>
{{/if}}
</div>
</div>
</div>
</div>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
<PopupMenu>
<nav class="menu">
<ul class="menu-list">
<li>
<LinkTo
@route="keys.key.details"
@model={{pkiKey.keyId}}
@disabled={{not @canRead}}
data-test-key-menu-link="details"
>
Details
</LinkTo>
</li>
<li>
<LinkTo
@route="keys.key.edit"
@model={{pkiKey.keyId}}
@disabled={{not @canEdit}}
data-test-key-menu-link="edit"
>
Edit
</LinkTo>
</li>
</ul>
</nav>
</PopupMenu>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
<PopupMenu>
<nav class="menu">
<ul class="menu-list">
<li>
<LinkTo
@route="keys.key.details"
@model={{pkiKey.keyId}}
@disabled={{not @canRead}}
data-test-key-menu-link="details"
>
Details
</LinkTo>
</li>
<li>
<LinkTo
@route="keys.key.edit"
@model={{pkiKey.keyId}}
@disabled={{not @canEdit}}
data-test-key-menu-link="edit"
>
Edit
</LinkTo>
</li>
</ul>
</nav>
</PopupMenu>
</div>
</div>
</div>
</div>
</LinkedBlock>
{{/each}}
{{else}}
<EmptyState @title="No keys yet" @message="There are no keys in this PKI mount. You can generate or create one." />
{{/if}}
</LinkedBlock>
{{/each}}
</:list>

<:empty>
<EmptyState @title="No keys yet" @message="There are no keys in this PKI mount. You can generate or create one." />
</:empty>

<:configure>
<EmptyState @title="PKI not configured" @message={{this.notConfiguredMessage}}>
<LinkTo @route="configuration.create">
Configure PKI
</LinkTo>
</EmptyState>
</:configure>
</PkiPaginatedList>
Loading

0 comments on commit d3927bc

Please sign in to comment.