mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
PKI Certificate Details (#18737)
* adds pki certificate details page component * adds tests for pki base adapter * adds more comments * updates remaining pki/certificate model references to pki/certificate/base
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
import PkiCertAdapter from './cert';
|
||||
|
||||
export default class PkiCertificateAdapter extends PkiCertAdapter {}
|
||||
@@ -4,10 +4,46 @@ import ApplicationAdapter from '../../application';
|
||||
export default class PkiCertificateBaseAdapter extends ApplicationAdapter {
|
||||
namespace = 'v1';
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
getURL(backend, id) {
|
||||
const uri = `${this.buildURL()}/${encodePath(backend)}`;
|
||||
return id ? `${uri}/cert/${id}` : `${uri}/certs`;
|
||||
}
|
||||
|
||||
fetchByQuery(query) {
|
||||
const { backend, id } = query;
|
||||
const data = !id ? { list: true } : {};
|
||||
return this.ajax(this.getURL(backend, id), 'GET', { data }).then((resp) => {
|
||||
resp.data.backend = backend;
|
||||
if (id) {
|
||||
resp.data.id = id;
|
||||
resp.data.serial_number = id;
|
||||
}
|
||||
return resp;
|
||||
});
|
||||
}
|
||||
|
||||
query(store, type, query) {
|
||||
return this.fetchByQuery(query);
|
||||
}
|
||||
|
||||
queryRecord(store, type, query) {
|
||||
return this.fetchByQuery(query);
|
||||
}
|
||||
|
||||
// the only way to update a record is by revoking it which will set the revocationTime property
|
||||
updateRecord(store, type, snapshot) {
|
||||
const { backend, serialNumber, certificate } = snapshot.record;
|
||||
// Revoke certificate requires either serial_number or certificate
|
||||
const data = serialNumber ? { serial_number: serialNumber } : { certificate };
|
||||
return this.ajax(`${this.buildURL()}/${encodePath(backend)}/revoke`, 'POST', { data });
|
||||
return this.ajax(`${this.buildURL()}/${encodePath(backend)}/revoke`, 'POST', { data }).then(
|
||||
(response) => {
|
||||
return {
|
||||
data: {
|
||||
...this.serialize(snapshot),
|
||||
...response.data,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,15 @@ import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
* attributes and adapter methods.
|
||||
*/
|
||||
|
||||
const certDisplayFields = ['certificate', 'commonName', 'serialNumber', 'notValidAfter', 'notValidBefore'];
|
||||
const certDisplayFields = [
|
||||
'certificate',
|
||||
'commonName',
|
||||
'revocationTime',
|
||||
'issueDate',
|
||||
'serialNumber',
|
||||
'notValidBefore',
|
||||
'notValidAfter',
|
||||
];
|
||||
|
||||
@withFormFields(certDisplayFields)
|
||||
export default class PkiCertificateBaseModel extends Model {
|
||||
@@ -31,16 +39,18 @@ export default class PkiCertificateBaseModel extends Model {
|
||||
|
||||
// Attrs that come back from API POST request
|
||||
@attr() caChain;
|
||||
@attr('string') certificate;
|
||||
@attr('string', { masked: true }) certificate;
|
||||
@attr('number') expiration;
|
||||
@attr('number', { formatDate: true }) revocationTime;
|
||||
@attr('string') issuingCa;
|
||||
@attr('string') privateKey;
|
||||
@attr('string') privateKeyType;
|
||||
@attr('string') serialNumber;
|
||||
|
||||
// Parsed from cert in serializer
|
||||
@attr('date') notValidAfter;
|
||||
@attr('date') notValidBefore;
|
||||
@attr('number', { formatDate: true }) issueDate;
|
||||
@attr('number', { formatDate: true }) notValidAfter;
|
||||
@attr('number', { formatDate: true }) notValidBefore;
|
||||
|
||||
// For importing
|
||||
@attr('string') pemBundle;
|
||||
|
||||
31
ui/app/serializers/pki/certificate/base.js
Normal file
31
ui/app/serializers/pki/certificate/base.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { parseCertificate } from 'vault/helpers/parse-pki-cert';
|
||||
import ApplicationSerializer from '../../application';
|
||||
|
||||
export default class PkiCertificateBaseSerializer extends ApplicationSerializer {
|
||||
primaryKey = 'serial_number';
|
||||
|
||||
attrs = {
|
||||
role: { serialize: false },
|
||||
};
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
if (payload.data.certificate) {
|
||||
// Parse certificate back from the API and add to payload
|
||||
const parsedCert = parseCertificate(payload.data.certificate);
|
||||
// convert issueDate to same format as other date values
|
||||
// this can be moved into the parseCertificate helper once the old pki implementation is removed
|
||||
if (parsedCert.issue_date) {
|
||||
parsedCert.issue_date = parsedCert.issue_date.valueOf();
|
||||
}
|
||||
const json = super.normalizeResponse(
|
||||
store,
|
||||
primaryModelClass,
|
||||
{ ...payload, ...parsedCert },
|
||||
id,
|
||||
requestType
|
||||
);
|
||||
return json;
|
||||
}
|
||||
return super.normalizeResponse(...arguments);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,3 @@
|
||||
import { parseCertificate } from 'vault/helpers/parse-pki-cert';
|
||||
import ApplicationSerializer from '../../application';
|
||||
import PkiCertificateBaseSerializer from './base';
|
||||
|
||||
export default class PkiCertificateGenerateSerializer extends ApplicationSerializer {
|
||||
primaryKey = 'serial_number';
|
||||
attrs = {
|
||||
role: { serialize: false },
|
||||
};
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
if (requestType === 'createRecord' && payload.data.certificate) {
|
||||
// Parse certificate back from the API and add to payload
|
||||
const parsedCert = parseCertificate(payload.data.certificate);
|
||||
const json = super.normalizeResponse(
|
||||
store,
|
||||
primaryModelClass,
|
||||
{ ...payload, ...parsedCert },
|
||||
id,
|
||||
requestType
|
||||
);
|
||||
return json;
|
||||
}
|
||||
return super.normalizeResponse(...arguments);
|
||||
}
|
||||
}
|
||||
export default class PkiCertificateGenerateSerializer extends PkiCertificateBaseSerializer {}
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
import { parseCertificate } from 'vault/helpers/parse-pki-cert';
|
||||
import ApplicationSerializer from '../../application';
|
||||
import PkiCertificateBaseSerializer from './base';
|
||||
|
||||
export default class PkiCertificateSignSerializer extends ApplicationSerializer {
|
||||
primaryKey = 'serial_number';
|
||||
attrs = {
|
||||
type: { serialize: false },
|
||||
};
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
if (requestType === 'createRecord' && payload.data.certificate) {
|
||||
// Parse certificate back from the API and add to payload
|
||||
const parsedCert = parseCertificate(payload.data.certificate);
|
||||
const json = super.normalizeResponse(
|
||||
store,
|
||||
primaryModelClass,
|
||||
{ ...payload, ...parsedCert },
|
||||
id,
|
||||
requestType
|
||||
);
|
||||
return json;
|
||||
}
|
||||
return super.normalizeResponse(...arguments);
|
||||
}
|
||||
}
|
||||
export default class PkiCertificateGenerateSerializer extends PkiCertificateBaseSerializer {}
|
||||
|
||||
55
ui/lib/pki/addon/components/page/pki-certificate-details.hbs
Normal file
55
ui/lib/pki/addon/components/page/pki-certificate-details.hbs
Normal file
@@ -0,0 +1,55 @@
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<button type="button" class="toolbar-link" {{on "click" this.downloadCert}} data-test-pki-cert-download-button>
|
||||
Download
|
||||
<Chevron @direction="down" @isButton={{true}} />
|
||||
</button>
|
||||
{{#if @model.canRevoke}}
|
||||
<ConfirmAction
|
||||
@buttonClasses="toolbar-link"
|
||||
@onConfirmAction={{fn (perform this.revoke)}}
|
||||
@confirmTitle="Revoke certificate?"
|
||||
@confirmButtonText="Revoke"
|
||||
data-test-pki-cert-revoke-button
|
||||
>
|
||||
Revoke certificate
|
||||
</ConfirmAction>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
|
||||
{{#each @model.formFields as |field|}}
|
||||
{{#if (eq field.name "certificate")}}
|
||||
<InfoTableRow @label="Certificate">
|
||||
<MaskedInput @value={{@model.certificate}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{else if (eq field.name "serialNumber")}}
|
||||
<InfoTableRow @label="Serial number">
|
||||
<code class="has-text-black">{{@model.serialNumber}}</code>
|
||||
</InfoTableRow>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (humanize (dasherize field.name))}}
|
||||
{{! formatDate fields can be 0 which will cause them to always render -- pass null instead }}
|
||||
@value={{or (get @model field.name) null}}
|
||||
@formatDate={{if field.options.formatDate "MMM dd yyyy hh:mm:ss a"}}
|
||||
@alwaysRender={{false}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
{{#if @onBack}}
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
disabled={{this.revoke.isRunning}}
|
||||
{{on "click" @onBack}}
|
||||
data-test-pki-cert-details-back
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
48
ui/lib/pki/addon/components/page/pki-certificate-details.ts
Normal file
48
ui/lib/pki/addon/components/page/pki-certificate-details.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { waitFor } from '@ember/test-waiters';
|
||||
import errorMessage from 'vault/utils/error-message';
|
||||
import FlashMessageService from 'vault/services/flash-messages';
|
||||
import DownloadService from 'vault/services/download';
|
||||
import PkiCertificateBaseModel from 'vault/models/pki/certificate/base';
|
||||
|
||||
interface Args {
|
||||
model: PkiCertificateBaseModel;
|
||||
onRevoke?: CallableFunction;
|
||||
onBack?: CallableFunction;
|
||||
}
|
||||
|
||||
export default class PkiCertificateDetailsComponent extends Component<Args> {
|
||||
@service declare readonly flashMessages: FlashMessageService;
|
||||
@service declare readonly download: DownloadService;
|
||||
|
||||
@action
|
||||
downloadCert() {
|
||||
try {
|
||||
const formattedSerial = this.args.model.serialNumber?.replace(/(\s|:)+/g, '-');
|
||||
this.download.pem(formattedSerial, this.args.model.certificate);
|
||||
this.flashMessages.info('Your download has started.');
|
||||
} catch (err) {
|
||||
this.flashMessages.danger(errorMessage(err, 'Unable to prepare certificate for download.'));
|
||||
}
|
||||
}
|
||||
|
||||
@task
|
||||
@waitFor
|
||||
*revoke() {
|
||||
try {
|
||||
// the adapter updateRecord method calls the revoke endpoint since it is the only way to update a cert
|
||||
yield this.args.model.save();
|
||||
this.flashMessages.success('The certificate has been revoked.');
|
||||
if (this.args.onRevoke) {
|
||||
this.args.onRevoke();
|
||||
}
|
||||
} catch (error) {
|
||||
this.flashMessages.danger(
|
||||
errorMessage(error, 'Could not revoke certificate. See Vault logs for details.')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@
|
||||
<SearchSelect
|
||||
class="is-flex-1"
|
||||
@selectLimit="1"
|
||||
@models={{array "pki/certificate"}}
|
||||
@models={{array "pki/certificate/base"}}
|
||||
@backend={{@engine.id}}
|
||||
@placeholder="33:a3:..."
|
||||
@disallowNewItems={{true}}
|
||||
|
||||
@@ -1,50 +1,5 @@
|
||||
{{#if @model.serialNumber}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<button type="button" class="toolbar-link" {{on "click" this.downloadCert}} data-test-pki-cert-download-button>
|
||||
Download
|
||||
<Chevron @direction="down" @isButton={{true}} />
|
||||
</button>
|
||||
{{#if @model.canRevoke}}
|
||||
<button
|
||||
type="button"
|
||||
class="toolbar-link"
|
||||
{{on "click" (perform this.revoke)}}
|
||||
disabled={{this.revoke.isRunning}}
|
||||
data-test-pki-cert-revoke-button
|
||||
>
|
||||
Revoke certificate
|
||||
<Chevron @direction="right" @isButton={{true}} />
|
||||
</button>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{#each @model.formFields as |attr|}}
|
||||
{{#if (eq attr.name "certificate")}}
|
||||
<InfoTableRow @label="Certificate" @value={{@model.certificate}}>
|
||||
<MaskedInput @value={{@model.certificate}} @name="Certificate" @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@label={{or attr.options.label (humanize (dasherize attr.name))}}
|
||||
@value={{get @model attr.name}}
|
||||
@formatDate={{if (eq attr.type "date") "MMM d yyyy HH:mm:ss a zzzz"}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
type="button"
|
||||
class="button has-left-margin-s"
|
||||
disabled={{this.save.isRunning}}
|
||||
{{on "click" this.cancel}}
|
||||
data-test-pki-generate-back
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Page::PkiCertificateDetails @model={{@model}} @onRevoke={{this.cancel}} @onBack={{this.cancel}} />
|
||||
{{else}}
|
||||
<form {{on "submit" (perform this.save)}} data-test-pki-generate-cert-form>
|
||||
<div class="box is-bottomless is-fullwidth is-marginless">
|
||||
|
||||
@@ -24,10 +24,6 @@ export default class PkiRoleGenerate extends Component<Args> {
|
||||
|
||||
@tracked errorBanner = '';
|
||||
|
||||
transitionToRole() {
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.pki.roles.role.details');
|
||||
}
|
||||
|
||||
get verb() {
|
||||
return this.args.type === 'sign' ? 'sign' : 'generate';
|
||||
}
|
||||
@@ -45,29 +41,8 @@ export default class PkiRoleGenerate extends Component<Args> {
|
||||
}
|
||||
}
|
||||
|
||||
@task
|
||||
*revoke() {
|
||||
try {
|
||||
yield this.args.model.destroyRecord();
|
||||
this.flashMessages.success('The certificate has been revoked.');
|
||||
this.transitionToRole();
|
||||
} catch (err) {
|
||||
this.errorBanner = errorMessage(err, 'Could not revoke certificate. See Vault logs for details.');
|
||||
}
|
||||
}
|
||||
|
||||
@action downloadCert() {
|
||||
try {
|
||||
const formattedSerial = this.args.model.serialNumber?.replace(/(\s|:)+/g, '-');
|
||||
this.download.pem(formattedSerial, this.args.model.certificate);
|
||||
this.flashMessages.info('Your download has started.');
|
||||
} catch (err) {
|
||||
this.flashMessages.danger(errorMessage(err, 'Unable to prepare certificate for download.'));
|
||||
}
|
||||
}
|
||||
|
||||
@action cancel() {
|
||||
this.args.model.unloadRecord();
|
||||
this.transitionToRole();
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.pki.roles.role.details');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class PkiCertificateDetailsRoute extends Route {}
|
||||
export default class PkiCertificateDetailsRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
|
||||
model() {
|
||||
const id = this.paramsFor('certificates/certificate').serial;
|
||||
return this.store.queryRecord('pki/certificate/base', { backend: this.secretMountPath.currentPath, id });
|
||||
}
|
||||
setupController(controller, model) {
|
||||
super.setupController(controller, model);
|
||||
const backend = this.secretMountPath.currentPath || 'pki';
|
||||
controller.breadcrumbs = [
|
||||
{ label: 'secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: backend, route: 'overview' },
|
||||
{ label: 'certificates', route: 'certificates.index' },
|
||||
{ label: model.id },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,10 @@ import { hash } from 'rsvp';
|
||||
export default class PkiCertificatesIndexRoute extends PkiOverviewRoute {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
@service pathHelp;
|
||||
|
||||
beforeModel() {
|
||||
// Must call this promise before the model hook otherwise it doesn't add OpenApi to record.
|
||||
return this.pathHelp.getNewModel('pki/certificate', this.secretMountPath.currentPath);
|
||||
}
|
||||
|
||||
async fetchCertificates() {
|
||||
try {
|
||||
return await this.store.query('pki/certificate', { backend: this.secretMountPath.currentPath });
|
||||
return await this.store.query('pki/certificate/base', { backend: this.secretMountPath.currentPath });
|
||||
} catch (e) {
|
||||
if (e.httpStatus === 404) {
|
||||
return { parentModel: this.modelFor('certificates') };
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
route: certificate.details
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3">
|
||||
<Icon @name="certificate" @size="24" class="has-text-grey-light" />
|
||||
View certificate
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
<Page::PkiCertificateDetails @model={{this.model}} />
|
||||
@@ -0,0 +1,116 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { click, render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Integration | Component | pki | Page::PkiCertificateDetails', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupEngine(hooks, 'pki');
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
const downloadService = this.owner.lookup('service:download');
|
||||
this.downloadSpy = sinon.stub(downloadService, 'pem');
|
||||
|
||||
const routerService = this.owner.lookup('service:router');
|
||||
this.routerSpy = sinon.stub(routerService, 'transitionTo');
|
||||
|
||||
this.owner.lookup('service:secretMountPath').update('pki');
|
||||
|
||||
const store = this.owner.lookup('service:store');
|
||||
const id = '4d:b6:ed:90:d6:b0:d4:bb:8e:5d:73:6a:6f:32:dc:8c:71:7c:db:5f';
|
||||
store.pushPayload('pki/certificate/base', {
|
||||
modelName: 'pki/certificate/base',
|
||||
data: {
|
||||
certificate: '-----BEGIN CERTIFICATE-----',
|
||||
common_name: 'example.com Intermediate Authority',
|
||||
issue_date: 1673540867000,
|
||||
serial_number: id,
|
||||
not_valid_after: 1831220897000,
|
||||
not_valid_before: 1673540867000,
|
||||
},
|
||||
});
|
||||
this.model = store.peekRecord('pki/certificate/base', id);
|
||||
|
||||
this.server.post('/sys/capabilities-self', () => ({
|
||||
data: {
|
||||
capabilities: ['root'],
|
||||
'pki/revoke': ['root'],
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
test('it should render actions and fields', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
this.server.post('/pki/revoke', (schema, req) => {
|
||||
const data = JSON.parse(req.requestBody);
|
||||
assert.strictEqual(
|
||||
data.serial_number,
|
||||
this.model.serialNumber,
|
||||
'Revoke request made with serial number'
|
||||
);
|
||||
return {
|
||||
data: {
|
||||
revocation_time: 1673972804,
|
||||
revocation_time_rfc3339: '2023-01-17T16:26:44.960933411Z',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await render(hbs`<Page::PkiCertificateDetails @model={{this.model}} />`, { owner: this.engine });
|
||||
|
||||
assert
|
||||
.dom('[data-test-component="info-table-row"]')
|
||||
.exists({ count: 6 }, 'Correct number of fields render when certificate has not been revoked');
|
||||
assert
|
||||
.dom('[data-test-value-div="Certificate"] [data-test-masked-input]')
|
||||
.exists('Masked input renders for certificate');
|
||||
assert.dom('[data-test-value-div="Serial number"] code').exists('Serial number renders as monospace');
|
||||
|
||||
await click('[data-test-pki-cert-download-button]');
|
||||
const { serialNumber, certificate } = this.model;
|
||||
assert.ok(
|
||||
this.downloadSpy.calledWith(serialNumber.replace(/(\s|:)+/g, '-'), certificate),
|
||||
'Download pem method called with correct args'
|
||||
);
|
||||
|
||||
await click('[data-test-confirm-action-trigger]');
|
||||
await click('[data-test-confirm-button]');
|
||||
|
||||
assert.dom('[data-test-value-div="Revocation time"]').exists('Revocation time is displayed');
|
||||
});
|
||||
|
||||
test('it should render back button', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.cancel = () => assert.ok('onBack action is triggered');
|
||||
|
||||
await render(hbs`<Page::PkiCertificateDetails @model={{this.model}} @onBack={{this.cancel}} />`, {
|
||||
owner: this.engine,
|
||||
});
|
||||
await click('[data-test-pki-cert-details-back]');
|
||||
});
|
||||
|
||||
test('it should send action on revoke if provided', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.post('/pki/revoke', () => ({
|
||||
data: {
|
||||
revocation_time: 1673972804,
|
||||
revocation_time_rfc3339: '2023-01-17T16:26:44.960933411Z',
|
||||
},
|
||||
}));
|
||||
|
||||
this.revoked = () => assert.ok('onRevoke action is triggered');
|
||||
|
||||
await render(hbs`<Page::PkiCertificateDetails @model={{this.model}} @onRevoke={{this.revoked}} />`, {
|
||||
owner: this.engine,
|
||||
});
|
||||
await click('[data-test-confirm-action-trigger]');
|
||||
await click('[data-test-confirm-button]');
|
||||
});
|
||||
});
|
||||
@@ -21,8 +21,8 @@ module('Integration | Component | Page::PkiOverview', function (hooks) {
|
||||
this.store.createRecord('pki/role', { name: 'role-0' });
|
||||
this.store.createRecord('pki/role', { name: 'role-1' });
|
||||
this.store.createRecord('pki/role', { name: 'role-2' });
|
||||
this.store.createRecord('pki/certificate', { serialNumber: '22:2222:22222:2222' });
|
||||
this.store.createRecord('pki/certificate', { serialNumber: '33:3333:33333:3333' });
|
||||
this.store.createRecord('pki/certificate/base', { serialNumber: '22:2222:22222:2222' });
|
||||
this.store.createRecord('pki/certificate/base', { serialNumber: '33:3333:33333:3333' });
|
||||
|
||||
this.issuers = this.store.peekAll('pki/issuer');
|
||||
this.roles = this.store.peekAll('pki/role');
|
||||
|
||||
57
ui/tests/unit/adapters/pki/certificate/base-test.js
Normal file
57
ui/tests/unit/adapters/pki/certificate/base-test.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'vault/tests/helpers';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
|
||||
module('Unit | Adapter | pki/certificate/base', function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.owner.lookup('service:secretMountPath').update('pki-test');
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on queryRecord', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.get('/pki-test/cert/1234', () => {
|
||||
assert.ok(true, 'Request made to correct endpoint on queryRecord');
|
||||
return { data: {} };
|
||||
});
|
||||
|
||||
await this.store.queryRecord('pki/certificate/base', { backend: 'pki-test', id: '1234' });
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on query', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.get('/pki-test/certs', (schema, req) => {
|
||||
assert.strictEqual(req.queryParams.list, 'true', 'Request made to correct endpoint on query');
|
||||
return { data: { keys: [] } };
|
||||
});
|
||||
|
||||
await this.store.query('pki/certificate/base', { backend: 'pki-test' });
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on update', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.store.pushPayload('pki/certificate/base', {
|
||||
modelName: 'pki/certificate/base',
|
||||
data: {
|
||||
serial_number: '1234',
|
||||
},
|
||||
});
|
||||
|
||||
this.server.post('pki-test/revoke', (schema, req) => {
|
||||
assert.deepEqual(
|
||||
JSON.parse(req.requestBody),
|
||||
{ serial_number: '1234' },
|
||||
'Request made to correct endpoint on update'
|
||||
);
|
||||
return { data: {} };
|
||||
});
|
||||
|
||||
await this.store.peekRecord('pki/certificate/base', '1234').save();
|
||||
});
|
||||
});
|
||||
@@ -35,20 +35,4 @@ module('Unit | Adapter | pki/certificate/generate', function (hooks) {
|
||||
const model = await this.store.createRecord('pki/certificate/generate', generateData);
|
||||
await model.save();
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on delete', async function (assert) {
|
||||
assert.expect(2);
|
||||
this.store.pushPayload('pki/certificate/generate', {
|
||||
modelName: 'pki/certificate/generate',
|
||||
...this.data,
|
||||
});
|
||||
this.server.post(`${this.backend}/revoke`, (schema, req) => {
|
||||
assert.deepEqual(JSON.parse(req.requestBody), { serial_number: 'my-serial-number' });
|
||||
assert.ok(true, 'request made to correct endpoint on delete');
|
||||
return { data: {} };
|
||||
});
|
||||
|
||||
const model = await this.store.peekRecord('pki/certificate/generate', this.data.serial_number);
|
||||
await model.destroyRecord();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,19 +47,4 @@ module('Unit | Adapter | pki/certificate/sign', function (hooks) {
|
||||
|
||||
await this.store.createRecord('pki/certificate/sign', generateData).save();
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on delete', async function (assert) {
|
||||
assert.expect(2);
|
||||
this.store.pushPayload('pki/certificate/sign', {
|
||||
modelName: 'pki/certificate/sign',
|
||||
...this.data,
|
||||
});
|
||||
this.server.post(`${this.backend}/revoke`, (schema, req) => {
|
||||
assert.deepEqual(JSON.parse(req.requestBody), { serial_number: 'my-serial-number' });
|
||||
assert.ok(true, 'request made to correct endpoint on delete');
|
||||
return { data: {} };
|
||||
});
|
||||
|
||||
await this.store.peekRecord('pki/certificate/sign', this.data.serial_number).destroyRecord();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user