vault/ui/tests/integration/components/pki/pki-issuer-cross-sign-test.js
claire bontempo 76e742ba32
ui: HDS adoption replace <AlertBanner> component (#21375)
* UI: HDS adoption replace AlertBanner part 1 (#21163)

* rename test selector

* replace db banner

* add class

* replace db role edit

* db creds

* generate creds

* simpler class

* license banner component

* oidc callback plash

* raft

* aws

* secret create or update

* change to compact alert for form field

* change back to inline

* combine alert banners

* wrap in conditional

* remove references to message class

* UI: HDS adoption replace AlertBanner part 2 (#21243)

* token expire warning

* delete css

* edit form

* item details distribute mfa step 2 transit verify

* back to secondary

* distribute

* oidc lease error

* sign

* kv obj and repl dash

* more repl

* update test selector

* show, creds

* shamir

* pki csr

* pki banners

* add hds library to ember engines

* woops comma

* fix k8 test

* update message error component for last!

* hold off MessageError changes until next pr

* revert test selectors

* update pki tests

* UI: part 3 remove alert banner (#21334)

* final component swap

* and actual final of MessageError

* update MessageError selectors

* delete alert-banner and remove references

* update next step alerts to highlight color

* finishing touches, auth form test and client dashboard inline link

* fix more selectors

* fix shamir flow test

* ui: part 4 final cleanup (#21365)

* replace AlertPopup

* add test tag

* move tag

* one more message error tag

* delete alert popup

* final css cleanup

* move preformatted flash into <p> tag

* ui: address comments for sidebranch  (#21388)

* add periods, move link to trailing

* more periods and typo fix
2023-06-21 11:37:11 -07: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-cross-sign-alert-title]').hasText('Cross-sign failed');
assert
.dom('[data-test-cross-sign-alert-message]')
.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-cross-sign-alert-title]')
.hasText('Certificate must be manually cross-signed using the CLI.');
assert
.dom('[data-test-cross-sign-alert-message]')
.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-cross-sign-alert-title]').hasText('Cross-sign failed');
assert
.dom('[data-test-cross-sign-alert-message]')
.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`);
}
});
});