UI: VAULT-6519 PKI configuration read (#19677)
This commit is contained in:
parent
cde1b3e328
commit
84957ad993
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||
import ApplicationAdapter from '../application';
|
||||
|
||||
export default class PkiCrlAdapter extends ApplicationAdapter {
|
||||
namespace = 'v1';
|
||||
|
||||
_url(backend) {
|
||||
return `${this.buildURL()}/${encodePath(backend)}/config/crl`;
|
||||
}
|
||||
|
||||
findRecord(store, type, backend) {
|
||||
return this.ajax(this._url(backend), 'GET').then((resp) => {
|
||||
return resp.data;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -62,4 +62,6 @@ export default class MountConfigModel extends Model {
|
|||
noDefault: true,
|
||||
})
|
||||
tokenType;
|
||||
|
||||
@attr() allowedManagedKeys;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
|
||||
export default class PkiCrlModel extends Model {
|
||||
// This model uses the backend value as the model ID
|
||||
get useOpenAPI() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@attr('string') expiry;
|
||||
@attr('boolean') autoRebuild;
|
||||
@attr('string') ocspExpiry;
|
||||
@attr('boolean') ocspDisable;
|
||||
}
|
|
@ -158,6 +158,9 @@
|
|||
.has-padding-m {
|
||||
padding: $spacing-m;
|
||||
}
|
||||
.has-bottom-padding-s {
|
||||
padding-bottom: $spacing-s;
|
||||
}
|
||||
.has-padding-xs {
|
||||
padding: $spacing-xs;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<h2 class="title has-bottom-margin-xs has-top-margin-m is-4 has-border-bottom-light has-bottom-padding-s">
|
||||
Global URLs
|
||||
</h2>
|
||||
<InfoTableRow @label="Issuing certificates" @value={{or @urls.issuingCertificates "None"}} />
|
||||
<InfoTableRow
|
||||
@label="CRL distribution points"
|
||||
@value={{if @urls.crlDistributionPoints @urls.crlDistributionPoints "None"}}
|
||||
/>
|
||||
|
||||
<h2 class="title has-bottom-margin-xs has-top-margin-xl is-4 has-border-bottom-light has-bottom-padding-s">
|
||||
Certificate Revocation List (CRL)
|
||||
</h2>
|
||||
<InfoTableRow @label="Expiry" @value={{@crl.expiry}} />
|
||||
<InfoTableRow @label="Auto-rebuild" @value={{if @crl.autoRebuild "On" "Off"}} />
|
||||
|
||||
<h2 class="title has-bottom-margin-xs has-top-margin-xl is-4 has-border-bottom-light has-bottom-padding-s">
|
||||
Online Certificate Status Protocol (OCSP)
|
||||
</h2>
|
||||
<InfoTableRow @label="Responder APIs" @value={{if @crl.ocspDisable "Disabled" "Enabled"}} />
|
||||
<InfoTableRow @label="Interval" @value={{@crl.ocspExpiry}} />
|
||||
|
||||
<h2 class="title has-bottom-margin-xs has-top-margin-xl is-4 has-border-bottom-light has-bottom-padding-s">
|
||||
Mount Configuration
|
||||
</h2>
|
||||
<InfoTableRow @label="Secret engine type" @value={{@mountConfig.engineType}} />
|
||||
<InfoTableRow @label="Path" @value={{@mountConfig.path}} />
|
||||
<InfoTableRow @label="Accessor" @value={{@mountConfig.accessor}} />
|
||||
<InfoTableRow @label="Local" @value={{@mountConfig.local}} />
|
||||
<InfoTableRow @label="Seal wrap" @value={{@mountConfig.sealWrap}} />
|
||||
<InfoTableRow @label="Default lease TTL" @value={{@mountConfig.config.defaultLeaseTtl}} />
|
||||
<InfoTableRow @label="Max lease TTL" @value={{@mountConfig.config.maxLeaseTtl}} />
|
||||
<InfoTableRow @label="Allowed managed keys" @value={{or @mountConfig.config.allowedManagedKeys "None"}} />
|
||||
<div class="has-top-margin-l"></div>
|
|
@ -4,5 +4,31 @@
|
|||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfig } from 'pki/decorators/check-config';
|
||||
import { hash } from 'rsvp';
|
||||
|
||||
export default class ConfigurationIndexRoute extends Route {}
|
||||
@withConfig()
|
||||
export default class ConfigurationIndexRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
|
||||
model() {
|
||||
const backend = this.secretMountPath.currentPath;
|
||||
return hash({
|
||||
hasConfig: this.shouldPromptConfig,
|
||||
engine: this.modelFor('application'),
|
||||
urls: this.store.findRecord('pki/urls', backend),
|
||||
crl: this.store.findRecord('pki/crl', backend),
|
||||
mountConfig: this.fetchMountConfig(backend),
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMountConfig(path) {
|
||||
const mountConfig = await this.store.query('secret-engine', { path });
|
||||
|
||||
if (mountConfig) {
|
||||
return mountConfig.get('firstObject');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,33 @@
|
|||
<SecretListHeader
|
||||
@model={{this.model}}
|
||||
@model={{this.model.engine}}
|
||||
@backendCrumb={{hash
|
||||
label=this.model.id
|
||||
text=this.model.id
|
||||
label=this.model.engine.id
|
||||
text=this.model.engine.id
|
||||
path="vault.cluster.secrets.backend.list-root"
|
||||
model=this.model.id
|
||||
model=this.model.engine.id
|
||||
}}
|
||||
@isEngine={{true}}
|
||||
/>
|
||||
{{! <Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink @route="configuration.tidy">
|
||||
Tidy
|
||||
</ToolbarLink>
|
||||
<ToolbarLink @route="configuration.edit">
|
||||
Edit configuration
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar> }}
|
||||
|
||||
<AlertBanner
|
||||
@type="info"
|
||||
@title="PKI beta in use"
|
||||
@message="To view the secret engine configuration or tune the mount, you'll have to return to the regular version."
|
||||
class="has-top-margin-m"
|
||||
data-test-pki-configuration-banner
|
||||
>
|
||||
<LinkToExternal @route="secretsListRootConfiguration" class="info">
|
||||
Return to old PKI to configure
|
||||
</LinkToExternal>
|
||||
</AlertBanner>
|
||||
{{#if this.model.hasConfig}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink @route="configuration.tidy">
|
||||
Tidy
|
||||
</ToolbarLink>
|
||||
<ToolbarLink @route="configuration.edit">
|
||||
Edit configuration
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
|
||||
<Page::PkiConfigurationDetails @urls={{this.model.urls}} @crl={{this.model.crl}} @mountConfig={{this.model.mountConfig}} />
|
||||
{{else}}
|
||||
<Toolbar />
|
||||
|
||||
<EmptyState @title="PKI not configured" @message="This PKI mount hasn't yet been configured with a certificate issuer.">
|
||||
<LinkTo @route="configuration.create">
|
||||
Configure PKI
|
||||
</LinkTo>
|
||||
</EmptyState>
|
||||
{{/if}}
|
|
@ -75,15 +75,6 @@ module('Acceptance | pki workflow', function (hooks) {
|
|||
await click(SELECTORS.keysTab);
|
||||
assertEmptyState(assert, 'keys');
|
||||
});
|
||||
test('shows pki beta banner to return to old pki on new pki configuration page', async function (assert) {
|
||||
assert.expect(3);
|
||||
await authPage.login(this.pkiAdminToken);
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/configuration`);
|
||||
assert.dom(SELECTORS.configTab).exists('Configuration tab is present');
|
||||
assert.dom(SELECTORS.configuration.pkiBetaBanner).exists('Configuration beta banner exists');
|
||||
await click(SELECTORS.configuration.pkiBetaBannerLink);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/configuration`);
|
||||
});
|
||||
|
||||
module('configuration', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
export const SELECTORS = {
|
||||
// global urls
|
||||
issuingCertificatesLabel: '[data-test-row-label="Issuing certificates"]',
|
||||
issuingCertificatesRowVal: '[data-test-row-value="Issuing certificates"]',
|
||||
crlDistributionPointsLabel: '[data-test-row-label="CRL distribution points"]',
|
||||
crlDistributionPointsRowVal: '[data-test-row-value="CRL distribution points"]',
|
||||
// crl
|
||||
expiryLabel: '[data-test-row-label="Expiry"]',
|
||||
expiryRowVal: '[data-test-row-value="Expiry"]',
|
||||
rebuildLabel: '[data-test-row-label="Auto-rebuild"]',
|
||||
rebuildRowVal: '[data-test-row-value="Auto-rebuild"]',
|
||||
responderApiLabel: '[data-test-row-label="Responder APIs"]',
|
||||
responderApiRowVal: '[data-test-row-value="Responder APIs"]',
|
||||
intervalLabel: '[data-test-row-label="Interval"]',
|
||||
intervalRowVal: '[data-test-row-value="Interval"]',
|
||||
// mount configuration
|
||||
engineTypeLabel: '[data-test-row-label="Secret engine type"]',
|
||||
engineTypeRowVal: '[data-test-row-value="Secret engine type"]',
|
||||
pathLabel: '[data-test-row-label="Path"]',
|
||||
pathRowVal: '[data-test-row-value="Path"]',
|
||||
accessorLabel: '[data-test-row-label="Accessor"]',
|
||||
accessorRowVal: '[data-test-row-value="Accessor"]',
|
||||
localLabel: '[data-test-row-label="Local"]',
|
||||
localRowVal: '[data-test-value-div="Local"]',
|
||||
sealWrapLabel: '[data-test-row-label="Seal wrap"]',
|
||||
sealWrapRowVal: '[data-test-value-div="Seal wrap"]',
|
||||
maxLeaseTtlLabel: '[data-test-row-label="Max lease TTL"]',
|
||||
maxLeaseTtlRowVal: '[data-test-row-value="Max lease TTL"]',
|
||||
allowedManagedKeysLabel: '[data-test-row-label="Allowed managed keys"]',
|
||||
allowedManagedKeysRowVal: '[data-test-value-div="Allowed managed keys"]',
|
||||
};
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-configuration-details';
|
||||
|
||||
module('Integration | Component | Page::PkiConfigurationDetails', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupEngine(hooks, 'pki');
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.secretMountPath = this.owner.lookup('service:secret-mount-path');
|
||||
this.secretMountPath.currentPath = 'pki-test';
|
||||
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.urls = this.store.createRecord('pki/urls', { id: 'pki-test', issuingCertificates: 'example.com' });
|
||||
this.crl = this.store.createRecord('pki/crl', {
|
||||
id: 'pki-test',
|
||||
expiry: '20h',
|
||||
autoRebuild: false,
|
||||
ocspExpiry: '77h',
|
||||
oscpDisable: true,
|
||||
});
|
||||
this.mountConfig = {
|
||||
id: 'pki-test',
|
||||
engineType: 'pki',
|
||||
path: '/pki-test',
|
||||
accessor: 'pki_33345b0d',
|
||||
local: false,
|
||||
sealWrap: true,
|
||||
config: this.store.createRecord('mount-config', {
|
||||
defaultLease: '12h',
|
||||
maxLeaseTtl: '400h',
|
||||
allowedManagedKeys: true,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
test('shows the correct information on global urls section', async function (assert) {
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(SELECTORS.issuingCertificatesLabel)
|
||||
.hasText('Issuing certificates', 'issuing certificate row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.issuingCertificatesRowVal)
|
||||
.hasText('example.com', 'issuing certificate value renders');
|
||||
this.urls.issuingCertificates = null;
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
assert
|
||||
.dom(SELECTORS.issuingCertificatesRowVal)
|
||||
.hasText('None', 'issuing certificate value renders None if none is configured');
|
||||
assert
|
||||
.dom(SELECTORS.crlDistributionPointsLabel)
|
||||
.hasText('CRL distribution points', 'crl distribution points row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.crlDistributionPointsRowVal)
|
||||
.hasText('None', 'crl distribution points value renders None if none is configured');
|
||||
});
|
||||
|
||||
test('shows the correct information on crl section', async function (assert) {
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(SELECTORS.expiryLabel).hasText('Expiry', 'crl expiry row label renders');
|
||||
assert.dom(SELECTORS.expiryRowVal).hasText('20h', 'expiry value renders');
|
||||
assert.dom(SELECTORS.rebuildLabel).hasText('Auto-rebuild', 'auto rebuild label renders');
|
||||
assert
|
||||
.dom(SELECTORS.rebuildRowVal)
|
||||
.hasText('Off', 'auto-rebuild value renders off if auto rebuild is false');
|
||||
this.crl.autoRebuild = true;
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
assert
|
||||
.dom(SELECTORS.rebuildRowVal)
|
||||
.hasText('On', 'auto-rebuild value renders on if auto rebuild is true');
|
||||
assert.dom(SELECTORS.responderApiLabel).hasText('Responder APIs', 'responder apis row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.responderApiRowVal)
|
||||
.hasText('Enabled', 'responder apis value renders Enabled if oscpDisable is true');
|
||||
assert.dom(SELECTORS.intervalLabel).hasText('Interval', 'interval row label renders');
|
||||
assert.dom(SELECTORS.intervalRowVal).hasText('77h', 'interval value renders');
|
||||
});
|
||||
|
||||
test('shows the correct information on mount configuration section', async function (assert) {
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(SELECTORS.engineTypeLabel).hasText('Secret engine type', 'engine type row label renders');
|
||||
assert.dom(SELECTORS.engineTypeRowVal).hasText('pki', 'engine type row value renders');
|
||||
assert.dom(SELECTORS.pathLabel).hasText('Path', 'path row label renders');
|
||||
assert.dom(SELECTORS.pathRowVal).hasText('/pki-test', 'path row value renders');
|
||||
assert.dom(SELECTORS.accessorLabel).hasText('Accessor', 'accessor row label renders');
|
||||
assert.dom(SELECTORS.accessorRowVal).hasText('pki_33345b0d', 'accessor row value renders');
|
||||
assert.dom(SELECTORS.localLabel).hasText('Local', 'local row label renders');
|
||||
assert.dom(SELECTORS.localRowVal).hasText('No', 'local row value renders');
|
||||
assert.dom(SELECTORS.sealWrapLabel).hasText('Seal wrap', 'seal wrap row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.sealWrapRowVal)
|
||||
.hasText('Yes', 'seal wrap row value renders Yes if sealWrap is true');
|
||||
assert.dom(SELECTORS.maxLeaseTtlLabel).hasText('Max lease TTL', 'max lease label renders');
|
||||
assert.dom(SELECTORS.maxLeaseTtlRowVal).hasText('400h', 'max lease value renders');
|
||||
assert
|
||||
.dom(SELECTORS.allowedManagedKeysLabel)
|
||||
.hasText('Allowed managed keys', 'allowed managed keys label renders');
|
||||
assert.dom(SELECTORS.allowedManagedKeysRowVal).hasText('Yes', 'allowed managed keys value renders');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue