open-vault/ui/tests/integration/components/pki/pki-issuer-cross-sign-test.js
hc-github-team-secure-vault-core 1ab8ade40e
backport of commit 4d7d1755309c753a3d53d637048cc23dc474d876 (#20878)
Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
2023-05-30 23:01:35 +00:00

382 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 { setupMirage } from 'ember-cli-mirage/test-support';
import { Response } from 'miragejs';
import { hbs } from 'ember-cli-htmlbars';
import {
intIssuerCert,
newCSR,
newlySignedCert,
oldParentIssuerCert,
parentIssuerCert,
unsupportedOids,
} from 'vault/tests/helpers/pki/values';
import { SELECTORS } from 'vault/tests/helpers/pki/pki-issuer-cross-sign';
const FIELDS = [
{
label: 'Mount path',
key: 'intermediateMount',
placeholder: 'Mount path',
helpText: 'The mount in which your new certificate can be found.',
},
{
label: "Issuer's current name",
key: 'intermediateIssuer',
placeholder: 'Current issuer name',
helpText: 'The API name of the previous intermediate which was cross-signed.',
},
{
label: 'New issuer name',
key: 'newCrossSignedIssuer',
placeholder: 'Enter a new issuer name',
helpText: `This is your new issuers name in the API.`,
},
];
module('Integration | Component | pki issuer cross sign', function (hooks) {
setupRenderingTest(hooks);
setupEngine(hooks, 'pki');
setupMirage(hooks);
hooks.beforeEach(async function () {
const store = this.owner.lookup('service:store');
this.backend = 'my-parent-issuer-mount';
this.intMountPath = 'int-mount';
this.owner.lookup('service:secret-mount-path').update(this.backend);
// parent issuer
this.parentIssuerData = {
ca_chain: [parentIssuerCert],
certificate: parentIssuerCert,
crl_distribution_points: [],
issuer_id: '0c983955-6426-22b2-1b3f-c0bdca40fd15',
issuer_name: 'my-parent-issuer-name',
issuing_certificates: [],
key_id: '8b8d0017-a067-ac50-c5cf-475876f9aac5',
leaf_not_after_behavior: 'err',
manual_chain: null,
ocsp_servers: [],
revocation_signature_algorithm: 'SHA256WithRSA',
revoked: false,
usage: 'crl-signing,issuing-certificates,ocsp-signing,read-only',
};
// intermediate issuer
this.intIssuerData = {
ca_chain: [intIssuerCert, oldParentIssuerCert],
certificate: intIssuerCert,
crl_distribution_points: [],
issuer_id: '6c286455-7904-5698-bf86-8aba81e680e6',
issuer_name: 'source-int-name',
issuing_certificates: [],
key_id: '2e2b8baf-4dac-c46f-cee4-8afbc7f2d8b2',
leaf_not_after_behavior: 'err',
manual_chain: null,
ocsp_servers: [],
revocation_signature_algorithm: '',
revoked: false,
usage: 'crl-signing,issuing-certificates,ocsp-signing,read-only',
};
// newly cross signed issuer
this.newIssuerData = {
ca_chain: [newlySignedCert, parentIssuerCert],
certificate: newlySignedCert,
crl_distribution_points: [],
issuer_id: 'bc159ba8-930c-c894-e871-2f3e889e8e02',
issuer_name: 'newly-cross-signed-cert',
issuing_certificates: [],
key_id: '2e2b8baf-4dac-c46f-cee4-8afbc7f2d8b2',
leaf_not_after_behavior: 'err',
manual_chain: null,
ocsp_servers: [],
revocation_signature_algorithm: '',
revoked: false,
usage: 'crl-signing,issuing-certificates,ocsp-signing,read-only',
};
this.testInputs = {
intermediateMount: this.intMountPath,
intermediateIssuer: this.intIssuerData.issuer_name,
newCrossSignedIssuer: this.newIssuerData.issuer_name,
};
store.pushPayload('pki/issuer', { modelName: 'pki/issuer', data: this.parentIssuerData });
this.parentIssuerModel = store.peekRecord('pki/issuer', this.parentIssuerData.issuer_id);
});
test('it makes requests to the correct endpoints', async function (assert) {
assert.expect(18);
this.server.get(`/${this.intMountPath}/issuer/${this.intIssuerData.issuer_name}`, () => {
assert.ok(true, 'Step 1. GET request is made to fetch existing issuer data');
return { data: this.intIssuerData };
});
this.server.post(`/${this.intMountPath}/intermediate/generate/existing`, (schema, req) => {
assert.ok(true, 'Step 2. POST request is made to generate new CSR');
assert.propEqual(
JSON.parse(req.requestBody),
{
common_name: newCSR.common_name,
country: null,
exclude_cn_from_sans: false,
format: 'pem',
locality: null,
organization: null,
ou: null,
province: null,
key_ref: this.intIssuerData.key_id,
},
'payload contains correct key ref'
);
return { data: { csr: newCSR.csr, key_id: this.intIssuerData.key_id } };
});
this.server.post(
`/${this.backend}/issuer/${this.parentIssuerData.issuer_name}/sign-intermediate`,
(schema, req) => {
assert.ok(true, 'Step 3. POST request is made to sign CSR with new parent issuer');
assert.propEqual(JSON.parse(req.requestBody), newCSR, 'payload has common name and csr');
return { data: { ca_chain: [newlySignedCert, parentIssuerCert] } };
}
);
this.server.post(`/${this.intMountPath}/issuers/import/bundle`, (schema, req) => {
assert.ok(true, 'Step 4. POST request made to import issuer');
assert.propEqual(
JSON.parse(req.requestBody),
{ pem_bundle: [newlySignedCert, parentIssuerCert].join('\n') },
'payload contains pem bundle'
);
return {
data: {
imported_issuers: null,
imported_keys: null,
mapping: { [this.newIssuerData.issuer_id]: this.intIssuerData.key_id },
},
};
});
this.server.get(`/${this.intMountPath}/issuer/${this.newIssuerData.issuer_id}`, () => {
assert.ok(true, 'Step 5. GET request is made to newly imported issuer');
return { data: this.newIssuerData };
});
this.server.post(`/${this.intMountPath}/issuer/${this.newIssuerData.issuer_id}`, (schema, req) => {
assert.ok(true, 'Step 6. POST request is made to update issuer name');
assert.propEqual(
JSON.parse(req.requestBody),
{
issuer_name: 'newly-cross-signed-cert',
leaf_not_after_behavior: 'err',
usage: 'crl-signing,issuing-certificates,ocsp-signing,read-only',
},
'payload has correct data '
);
return { data: this.newIssuerData };
});
await render(hbs`<PkiIssuerCrossSign @parentIssuer={{this.parentIssuerModel}} /> `, {
owner: this.engine,
});
// fill out form and submit
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key), this.testInputs[field.key]);
}
await click(SELECTORS.submitButton);
assert.dom(SELECTORS.statusCount).hasText('Cross-signing complete (1 successful, 0 errors)');
assert
.dom(`${SELECTORS.signedIssuerRow()} [data-test-icon="check-circle"]`)
.exists('row has success icon');
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(this.testInputs[field.key], `${field.key} displays correct value`);
assert.dom(`${SELECTORS.signedIssuerCol(field.key)} a`).hasTagName('a');
}
});
test('it cross-signs multiple certs', async function (assert) {
assert.expect(13);
const nonexistentIssuer = {
intermediateMount: this.intMountPath,
intermediateIssuer: 'some-fake-issuer',
newCrossSignedIssuer: 'failed-cert-1',
};
const unsupportedCert = {
intermediateMount: this.intMountPath,
intermediateIssuer: 'some-fancy-issuer',
newCrossSignedIssuer: 'failed-cert-2',
};
this.server.get(`/${this.intMountPath}/issuer/${this.intIssuerData.issuer_name}`, () => {
assert.ok(true, 'request is made to sign first cert');
return { data: this.intIssuerData };
});
this.server.get(`/${this.intMountPath}/issuer/${nonexistentIssuer.intermediateIssuer}`, () => {
assert.ok(true, 'request is made to second cert');
return new Response(
500,
{ 'Content-Type': 'application/json' },
JSON.stringify({
errors: [
`1 error occurred:\n\t* unable to find PKI issuer for reference: ${nonexistentIssuer.intermediateIssuer}\n\n`,
],
})
);
});
this.server.get(`/${this.intMountPath}/issuer/${unsupportedCert.intermediateIssuer}`, () => {
assert.ok(true, 'request is made to third cert');
return { data: { isser_name: unsupportedCert.intermediateIssuer, certificate: unsupportedOids } };
});
await render(hbs`<PkiIssuerCrossSign @parentIssuer={{this.parentIssuerModel}} /> `, {
owner: this.engine,
});
// fill out form and submit
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key), this.testInputs[field.key]);
}
await click(SELECTORS.addRow);
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key, 1), nonexistentIssuer[field.key]);
}
await click(SELECTORS.addRow);
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key, 2), unsupportedCert[field.key]);
}
await click(SELECTORS.submitButton);
assert.dom(SELECTORS.statusCount).hasText('Cross-signing complete (0 successful, 3 errors)');
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerRow()} ${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(this.testInputs[field.key], `first row has correct values`);
}
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerRow(1)} ${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(nonexistentIssuer[field.key], `second row has correct values`);
}
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerRow(2)} ${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(unsupportedCert[field.key], `third row has correct values`);
}
});
test('it returns API errors when a request fails', async function (assert) {
assert.expect(7);
this.server.get(`/${this.intMountPath}/issuer/${this.intIssuerData.issuer_name}`, () => {
return new Response(
500,
{ 'Content-Type': 'application/json' },
JSON.stringify({
errors: ['1 error occurred:\n\t* unable to find PKI issuer for reference: nonexistent-mount\n\n'],
})
);
});
await render(hbs`<PkiIssuerCrossSign @parentIssuer={{this.parentIssuerModel}} /> `, {
owner: this.engine,
});
// fill out form and submit
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key), this.testInputs[field.key]);
}
await click(SELECTORS.submitButton);
assert.dom(SELECTORS.statusCount).hasText('Cross-signing complete (0 successful, 1 error)');
assert
.dom(`${SELECTORS.signedIssuerRow()} [data-test-icon="alert-circle-fill"]`)
.exists('row has failure icon');
assert.dom('[data-test-alert-banner="alert"] .message-title').hasText('Cross-sign failed');
assert
.dom('[data-test-alert-banner="alert"] .alert-banner-message-body')
.hasText('1 error occurred: * unable to find PKI issuer for reference: nonexistent-mount');
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(this.testInputs[field.key], `${field.key} displays correct value`);
}
});
test('it returns an error when a certificate contains unsupported values', async function (assert) {
assert.expect(7);
const unsupportedIssuerCert = { ...this.intIssuerData, certificate: unsupportedOids };
this.server.get(`/${this.intMountPath}/issuer/${this.intIssuerData.issuer_name}`, () => {
return { data: unsupportedIssuerCert };
});
await render(hbs`<PkiIssuerCrossSign @parentIssuer={{this.parentIssuerModel}} /> `, {
owner: this.engine,
});
// fill out form and submit
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key), this.testInputs[field.key]);
}
await click(SELECTORS.submitButton);
assert.dom(SELECTORS.statusCount).hasText('Cross-signing complete (0 successful, 1 error)');
assert
.dom(`${SELECTORS.signedIssuerRow()} [data-test-icon="alert-circle-fill"]`)
.exists('row has failure icon');
assert
.dom('[data-test-alert-banner="alert"] .message-title')
.hasText('Certificate must be manually cross-signed using the CLI.');
assert
.dom('[data-test-alert-banner="alert"] .alert-banner-message-body')
.hasText(
'certificate contains unsupported subject OIDs: 1.2.840.113549.1.9.1, certificate contains unsupported extension OIDs: 2.5.29.37'
);
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(this.testInputs[field.key], `${field.key} displays correct value`);
}
});
test('it returns an error when attempting to self-cross-sign', async function (assert) {
assert.expect(7);
this.testInputs = {
intermediateMount: this.backend,
intermediateIssuer: this.parentIssuerData.issuer_name,
newCrossSignedIssuer: this.newIssuerData.issuer_name,
};
this.server.get(`/${this.backend}/issuer/${this.parentIssuerData.issuer_name}`, () => {
return { data: this.parentIssuerData };
});
await render(hbs`<PkiIssuerCrossSign @parentIssuer={{this.parentIssuerModel}} /> `, {
owner: this.engine,
});
// fill out form and submit
for (const field of FIELDS) {
await fillIn(SELECTORS.objectListInput(field.key), this.testInputs[field.key]);
}
await click(SELECTORS.submitButton);
assert.dom(SELECTORS.statusCount).hasText('Cross-signing complete (0 successful, 1 error)');
assert
.dom(`${SELECTORS.signedIssuerRow()} [data-test-icon="alert-circle-fill"]`)
.exists('row has failure icon');
assert.dom('[data-test-alert-banner="alert"] .message-title').hasText('Cross-sign failed');
assert
.dom('[data-test-alert-banner="alert"] .alert-banner-message-body')
.hasText('Cross-signing a root issuer with itself must be performed manually using the CLI.');
for (const field of FIELDS) {
assert
.dom(`${SELECTORS.signedIssuerCol(field.key)}`)
.hasText(this.testInputs[field.key], `${field.key} displays correct value`);
}
});
});