mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-18 21:21:06 +02:00
* update error handling for pki config * address comments! * VAULT-17194 link jira and rename errors var * add tests * Update ui/lib/pki/addon/components/page/pki-configuration-edit.ts * add test comment * combine save logic, update interface * stub router
459 lines
17 KiB
JavaScript
459 lines
17 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*/
|
|
|
|
import { module, test } from 'qunit';
|
|
import { setupRenderingTest } from 'vault/tests/helpers';
|
|
import { click, fillIn, render } from '@ember/test-helpers';
|
|
import { setupEngine } from 'ember-engines/test-support';
|
|
import { hbs } from 'ember-cli-htmlbars';
|
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
|
import { Response } from 'miragejs';
|
|
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-configuration-edit';
|
|
import sinon from 'sinon';
|
|
import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs';
|
|
|
|
module('Integration | Component | page/pki-configuration-edit', function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
setupEngine(hooks, 'pki');
|
|
setupMirage(hooks);
|
|
|
|
hooks.beforeEach(async function () {
|
|
// test context setup
|
|
this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub());
|
|
this.context = { owner: this.engine }; // this.engine set by setupEngine
|
|
this.store = this.owner.lookup('service:store');
|
|
this.router = this.owner.lookup('service:router');
|
|
sinon.stub(this.router, 'transitionTo');
|
|
|
|
// component data setup
|
|
this.backend = 'pki-engine';
|
|
// both models only use findRecord. API parameters for pki/crl
|
|
// are set by default backend values when the engine is mounted
|
|
this.store.pushPayload('pki/config/cluster', {
|
|
modelName: 'pki/config/cluster',
|
|
id: this.backend,
|
|
});
|
|
this.store.pushPayload('pki/config/acme', {
|
|
modelName: 'pki/config/acme',
|
|
id: this.backend,
|
|
});
|
|
this.store.pushPayload('pki/config/crl', {
|
|
modelName: 'pki/config/crl',
|
|
id: this.backend,
|
|
auto_rebuild: false,
|
|
auto_rebuild_grace_period: '12h',
|
|
delta_rebuild_interval: '15m',
|
|
disable: false,
|
|
enable_delta: false,
|
|
expiry: '72h',
|
|
ocsp_disable: false,
|
|
ocsp_expiry: '12h',
|
|
});
|
|
this.store.pushPayload('pki/config/urls', {
|
|
modelName: 'pki/config/urls',
|
|
id: this.backend,
|
|
issuing_certificates: ['hashicorp.com'],
|
|
crl_distribution_points: ['some-crl-distribution.com'],
|
|
ocsp_servers: ['ocsp-stuff.com'],
|
|
});
|
|
this.acme = this.store.peekRecord('pki/config/acme', this.backend);
|
|
this.cluster = this.store.peekRecord('pki/config/cluster', this.backend);
|
|
this.crl = this.store.peekRecord('pki/config/crl', this.backend);
|
|
this.urls = this.store.peekRecord('pki/config/urls', this.backend);
|
|
});
|
|
|
|
hooks.afterEach(function () {
|
|
this.router.transitionTo.restore();
|
|
});
|
|
|
|
test('it renders with config data and updates config', async function (assert) {
|
|
assert.expect(32);
|
|
this.server.post(`/${this.backend}/config/acme`, (schema, req) => {
|
|
assert.ok(true, 'request made to save acme config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
allowed_issuers: ['*'],
|
|
allowed_roles: ['my-role'],
|
|
dns_resolver: 'some-dns',
|
|
eab_policy: 'new-account-required',
|
|
enabled: true,
|
|
},
|
|
'it updates acme config model attributes'
|
|
);
|
|
});
|
|
this.server.post(`/${this.backend}/config/cluster`, (schema, req) => {
|
|
assert.ok(true, 'request made to save cluster config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
path: 'https://pr-a.vault.example.com/v1/ns1/pki-root',
|
|
aia_path: 'http://another-path.com',
|
|
},
|
|
'it updates cluster config model attributes'
|
|
);
|
|
});
|
|
this.server.post(`/${this.backend}/config/crl`, (schema, req) => {
|
|
assert.ok(true, 'request made to save crl config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
auto_rebuild: true,
|
|
auto_rebuild_grace_period: '24h',
|
|
delta_rebuild_interval: '45m',
|
|
disable: false,
|
|
enable_delta: true,
|
|
expiry: '1152h',
|
|
ocsp_disable: false,
|
|
ocsp_expiry: '24h',
|
|
},
|
|
'it updates crl config model attributes'
|
|
);
|
|
});
|
|
this.server.post(`/${this.backend}/config/urls`, (schema, req) => {
|
|
assert.ok(true, 'request made to save urls config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
crl_distribution_points: ['test-crl.com'],
|
|
issuing_certificates: ['update-hashicorp.com'],
|
|
ocsp_servers: ['ocsp.com'],
|
|
},
|
|
'it updates url config model attributes'
|
|
);
|
|
});
|
|
await render(
|
|
hbs`
|
|
<Page::PkiConfigurationEdit
|
|
@acme={{this.acme}}
|
|
@cluster={{this.cluster}}
|
|
@urls={{this.urls}}
|
|
@crl={{this.crl}}
|
|
@backend={{this.backend}}
|
|
/>
|
|
`,
|
|
this.context
|
|
);
|
|
|
|
assert.dom(SELECTORS.configEditSection).exists('renders config section');
|
|
assert.dom(SELECTORS.urlsEditSection).exists('renders urls section');
|
|
assert.dom(SELECTORS.crlEditSection).exists('renders crl section');
|
|
assert.dom(SELECTORS.cancelButton).exists();
|
|
this.urls.eachAttribute((name) => {
|
|
assert.dom(SELECTORS.urlFieldInput(name)).exists(`renders ${name} input`);
|
|
});
|
|
assert.dom(SELECTORS.urlFieldInput('issuingCertificates')).hasValue('hashicorp.com');
|
|
assert.dom(SELECTORS.urlFieldInput('crlDistributionPoints')).hasValue('some-crl-distribution.com');
|
|
assert.dom(SELECTORS.urlFieldInput('ocspServers')).hasValue('ocsp-stuff.com');
|
|
|
|
// cluster config
|
|
await fillIn(SELECTORS.configInput('path'), 'https://pr-a.vault.example.com/v1/ns1/pki-root');
|
|
await fillIn(SELECTORS.configInput('aiaPath'), 'http://another-path.com');
|
|
|
|
// acme config;
|
|
await click(SELECTORS.configInput('enabled'));
|
|
await fillIn(SELECTORS.stringListInput('allowedRoles'), 'my-role');
|
|
await fillIn(SELECTORS.stringListInput('allowedIssuers'), '*');
|
|
await fillIn(SELECTORS.configInput('eabPolicy'), 'new-account-required');
|
|
await fillIn(SELECTORS.configInput('dnsResolver'), 'some-dns');
|
|
|
|
// urls
|
|
await fillIn(SELECTORS.urlFieldInput('issuingCertificates'), 'update-hashicorp.com');
|
|
await fillIn(SELECTORS.urlFieldInput('crlDistributionPoints'), 'test-crl.com');
|
|
await fillIn(SELECTORS.urlFieldInput('ocspServers'), 'ocsp.com');
|
|
|
|
// confirm default toggle state and text
|
|
this.crl.eachAttribute((name, { options }) => {
|
|
if (['expiry', 'ocspExpiry'].includes(name)) {
|
|
assert.dom(SELECTORS.crlToggleInput(name)).isChecked(`${name} defaults to toggled on`);
|
|
assert.dom(SELECTORS.crlFieldLabel(name)).hasTextContaining(options.label);
|
|
assert.dom(SELECTORS.crlFieldLabel(name)).hasTextContaining(options.helperTextEnabled);
|
|
}
|
|
if (['autoRebuildGracePeriod', 'deltaRebuildInterval'].includes(name)) {
|
|
assert.dom(SELECTORS.crlToggleInput(name)).isNotChecked(`${name} defaults off`);
|
|
assert.dom(SELECTORS.crlFieldLabel(name)).hasTextContaining(options.labelDisabled);
|
|
assert.dom(SELECTORS.crlFieldLabel(name)).hasTextContaining(options.helperTextDisabled);
|
|
}
|
|
});
|
|
|
|
// toggle everything on
|
|
await click(SELECTORS.crlToggleInput('autoRebuildGracePeriod'));
|
|
assert
|
|
.dom(SELECTORS.crlFieldLabel('autoRebuildGracePeriod'))
|
|
.hasTextContaining(
|
|
'Auto-rebuild on Vault will rebuild the CRL in the below grace period before expiration',
|
|
'it renders auto rebuild toggled on text'
|
|
);
|
|
await click(SELECTORS.crlToggleInput('deltaRebuildInterval'));
|
|
assert
|
|
.dom(SELECTORS.crlFieldLabel('deltaRebuildInterval'))
|
|
.hasTextContaining(
|
|
'Delta CRL building on Vault will rebuild the delta CRL at the interval below:',
|
|
'it renders delta crl build toggled on text'
|
|
);
|
|
|
|
// assert ttl values update model attributes
|
|
await fillIn(SELECTORS.crlTtlInput('Expiry'), '48');
|
|
await fillIn(SELECTORS.crlTtlInput('Auto-rebuild on'), '24');
|
|
await fillIn(SELECTORS.crlTtlInput('Delta CRL building on'), '45');
|
|
await fillIn(SELECTORS.crlTtlInput('OCSP responder APIs enabled'), '24');
|
|
|
|
await click(SELECTORS.saveButton);
|
|
});
|
|
|
|
test('it removes urls and sends false crl values', async function (assert) {
|
|
assert.expect(8);
|
|
this.server.post(`/${this.backend}/config/acme`, () => {});
|
|
this.server.post(`/${this.backend}/config/cluster`, () => {});
|
|
this.server.post(`/${this.backend}/config/crl`, (schema, req) => {
|
|
assert.ok(true, 'request made to save crl config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
auto_rebuild: false,
|
|
auto_rebuild_grace_period: '12h',
|
|
delta_rebuild_interval: '15m',
|
|
disable: true,
|
|
enable_delta: false,
|
|
expiry: '72h',
|
|
ocsp_disable: true,
|
|
ocsp_expiry: '12h',
|
|
},
|
|
'crl payload has correct data'
|
|
);
|
|
});
|
|
this.server.post(`/${this.backend}/config/urls`, (schema, req) => {
|
|
assert.ok(true, 'request made to save urls config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
crl_distribution_points: [],
|
|
issuing_certificates: [],
|
|
ocsp_servers: [],
|
|
},
|
|
'url payload has empty arrays'
|
|
);
|
|
});
|
|
await render(
|
|
hbs`
|
|
<Page::PkiConfigurationEdit
|
|
@acme={{this.acme}}
|
|
@cluster={{this.cluster}}
|
|
@urls={{this.urls}}
|
|
@crl={{this.crl}}
|
|
@backend={{this.backend}}
|
|
/>
|
|
`,
|
|
this.context
|
|
);
|
|
|
|
await click(SELECTORS.deleteButton('issuingCertificates'));
|
|
await click(SELECTORS.deleteButton('crlDistributionPoints'));
|
|
await click(SELECTORS.deleteButton('ocspServers'));
|
|
|
|
// toggle everything off
|
|
await click(SELECTORS.crlToggleInput('expiry'));
|
|
assert.dom(SELECTORS.crlFieldLabel('expiry')).hasText('No expiry The CRL will not be built.');
|
|
assert
|
|
.dom(SELECTORS.crlToggleInput('autoRebuildGracePeriod'))
|
|
.doesNotExist('expiry off hides the auto rebuild toggle');
|
|
assert
|
|
.dom(SELECTORS.crlToggleInput('deltaRebuildInterval'))
|
|
.doesNotExist('expiry off hides delta crl toggle');
|
|
await click(SELECTORS.crlToggleInput('ocspExpiry'));
|
|
assert
|
|
.dom(SELECTORS.crlFieldLabel('ocspExpiry'))
|
|
.hasTextContaining(
|
|
'OCSP responder APIs disabled Requests cannot be made to check if an individual certificate is valid.',
|
|
'it renders correct toggled off text'
|
|
);
|
|
|
|
await click(SELECTORS.saveButton);
|
|
});
|
|
|
|
test('it renders enterprise only params', async function (assert) {
|
|
assert.expect(6);
|
|
this.version = this.owner.lookup('service:version');
|
|
this.version.version = '1.13.1+ent';
|
|
this.server.post(`/${this.backend}/config/acme`, () => {});
|
|
this.server.post(`/${this.backend}/config/cluster`, () => {});
|
|
this.server.post(`/${this.backend}/config/crl`, (schema, req) => {
|
|
assert.ok(true, 'request made to save crl config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
auto_rebuild: false,
|
|
auto_rebuild_grace_period: '12h',
|
|
delta_rebuild_interval: '15m',
|
|
disable: false,
|
|
enable_delta: false,
|
|
expiry: '72h',
|
|
ocsp_disable: false,
|
|
ocsp_expiry: '12h',
|
|
cross_cluster_revocation: true,
|
|
unified_crl: true,
|
|
unified_crl_on_existing_paths: true,
|
|
},
|
|
'crl payload includes enterprise params'
|
|
);
|
|
});
|
|
this.server.post(`/${this.backend}/config/urls`, () => {
|
|
assert.ok(true, 'request made to save urls config');
|
|
});
|
|
await render(
|
|
hbs`
|
|
<Page::PkiConfigurationEdit
|
|
@acme={{this.acme}}
|
|
@cluster={{this.cluster}}
|
|
@urls={{this.urls}}
|
|
@crl={{this.crl}}
|
|
@backend={{this.backend}}
|
|
/>
|
|
`,
|
|
this.context
|
|
);
|
|
|
|
assert.dom(SELECTORS.groupHeader('Certificate Revocation List (CRL)')).exists();
|
|
assert.dom(SELECTORS.groupHeader('Online Certificate Status Protocol (OCSP)')).exists();
|
|
assert.dom(SELECTORS.groupHeader('Unified Revocation')).exists();
|
|
await click(SELECTORS.checkboxInput('crossClusterRevocation'));
|
|
await click(SELECTORS.checkboxInput('unifiedCrl'));
|
|
await click(SELECTORS.checkboxInput('unifiedCrlOnExistingPaths'));
|
|
await click(SELECTORS.saveButton);
|
|
});
|
|
|
|
test('it does not render enterprise only params for OSS', async function (assert) {
|
|
assert.expect(9);
|
|
this.version = this.owner.lookup('service:version');
|
|
this.version.version = '1.13.1';
|
|
this.server.post(`/${this.backend}/config/acme`, () => {});
|
|
this.server.post(`/${this.backend}/config/cluster`, () => {});
|
|
this.server.post(`/${this.backend}/config/crl`, (schema, req) => {
|
|
assert.ok(true, 'request made to save crl config');
|
|
assert.propEqual(
|
|
JSON.parse(req.requestBody),
|
|
{
|
|
auto_rebuild: false,
|
|
auto_rebuild_grace_period: '12h',
|
|
delta_rebuild_interval: '15m',
|
|
disable: false,
|
|
enable_delta: false,
|
|
expiry: '72h',
|
|
ocsp_disable: false,
|
|
ocsp_expiry: '12h',
|
|
},
|
|
'crl payload does not include enterprise params'
|
|
);
|
|
});
|
|
this.server.post(`/${this.backend}/config/urls`, () => {
|
|
assert.ok(true, 'request made to save urls config');
|
|
});
|
|
await render(
|
|
hbs`
|
|
<Page::PkiConfigurationEdit
|
|
@acme={{this.acme}}
|
|
@cluster={{this.cluster}}
|
|
@urls={{this.urls}}
|
|
@crl={{this.crl}}
|
|
@backend={{this.backend}}
|
|
/>
|
|
`,
|
|
this.context
|
|
);
|
|
|
|
assert.dom(SELECTORS.checkboxInput('crossClusterRevocation')).doesNotExist();
|
|
assert.dom(SELECTORS.checkboxInput('unifiedCrl')).doesNotExist();
|
|
assert.dom(SELECTORS.checkboxInput('unifiedCrlOnExistingPaths')).doesNotExist();
|
|
assert.dom(SELECTORS.groupHeader('Certificate Revocation List (CRL)')).exists();
|
|
assert.dom(SELECTORS.groupHeader('Online Certificate Status Protocol (OCSP)')).exists();
|
|
assert.dom(SELECTORS.groupHeader('Unified Revocation')).doesNotExist();
|
|
await click(SELECTORS.saveButton);
|
|
});
|
|
|
|
test('it renders empty states if no update capabilities', async function (assert) {
|
|
assert.expect(4);
|
|
this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub(['read']));
|
|
|
|
await render(
|
|
hbs`
|
|
<Page::PkiConfigurationEdit
|
|
@acme={{this.acme}}
|
|
@cluster={{this.cluster}}
|
|
@urls={{this.urls}}
|
|
@crl={{this.crl}}
|
|
@backend={{this.backend}}
|
|
/>
|
|
`,
|
|
this.context
|
|
);
|
|
|
|
assert
|
|
.dom(`${SELECTORS.configEditSection} [data-test-component="empty-state"]`)
|
|
.hasText(
|
|
"You do not have permission to set this mount's the cluster config Ask your administrator if you think you should have access to: POST /pki-engine/config/cluster"
|
|
);
|
|
assert
|
|
.dom(`${SELECTORS.acmeEditSection} [data-test-component="empty-state"]`)
|
|
.hasText(
|
|
"You do not have permission to set this mount's ACME config Ask your administrator if you think you should have access to: POST /pki-engine/config/acme"
|
|
);
|
|
assert
|
|
.dom(`${SELECTORS.urlsEditSection} [data-test-component="empty-state"]`)
|
|
.hasText(
|
|
"You do not have permission to set this mount's URLs Ask your administrator if you think you should have access to: POST /pki-engine/config/urls"
|
|
);
|
|
assert
|
|
.dom(`${SELECTORS.crlEditSection} [data-test-component="empty-state"]`)
|
|
.hasText(
|
|
"You do not have permission to set this mount's revocation configuration Ask your administrator if you think you should have access to: POST /pki-engine/config/crl"
|
|
);
|
|
});
|
|
|
|
test('it renders alert banner and endpoint respective error', async function (assert) {
|
|
assert.expect(4);
|
|
this.server.post(`/${this.backend}/config/acme`, () => {
|
|
return new Response(500, {}, { errors: ['something wrong with acme'] });
|
|
});
|
|
this.server.post(`/${this.backend}/config/cluster`, () => {
|
|
return new Response(500, {}, { errors: ['something wrong with cluster'] });
|
|
});
|
|
this.server.post(`/${this.backend}/config/crl`, () => {
|
|
return new Response(500, {}, { errors: ['something wrong with crl'] });
|
|
});
|
|
this.server.post(`/${this.backend}/config/urls`, () => {
|
|
return new Response(500, {}, { errors: ['something wrong with urls'] });
|
|
});
|
|
await render(
|
|
hbs`
|
|
<Page::PkiConfigurationEdit
|
|
@acme={{this.acme}}
|
|
@cluster={{this.cluster}}
|
|
@urls={{this.urls}}
|
|
@crl={{this.crl}}
|
|
@backend={{this.backend}}
|
|
/>
|
|
`,
|
|
this.context
|
|
);
|
|
|
|
await click(SELECTORS.saveButton);
|
|
assert
|
|
.dom(SELECTORS.errorBanner)
|
|
.hasText(
|
|
'Error POST config/cluster: something wrong with cluster POST config/acme: something wrong with acme POST config/urls: something wrong with urls POST config/crl: something wrong with crl'
|
|
);
|
|
assert.dom(`${SELECTORS.errorBanner} ul`).hasClass('bullet');
|
|
|
|
// change 3 out of 4 requests to be successful to assert single error renders correctly
|
|
this.server.post(`/${this.backend}/config/acme`, () => new Response(200));
|
|
this.server.post(`/${this.backend}/config/cluster`, () => new Response(200));
|
|
this.server.post(`/${this.backend}/config/crl`, () => new Response(200));
|
|
|
|
await click(SELECTORS.saveButton);
|
|
assert.dom(SELECTORS.errorBanner).hasText('Error POST config/urls: something wrong with urls');
|
|
assert.dom(`${SELECTORS.errorBanner} ul`).doesNotHaveClass('bullet');
|
|
});
|
|
});
|