PKI Issuer Edit (#18687)
* adds pki issuer edit view * updates pki issuer details test and fixes styling issue in issuer edit form * addresses feedback
This commit is contained in:
parent
8fe50cfa37
commit
cd30860cb6
|
@ -37,6 +37,13 @@ export default class PkiIssuerAdapter extends ApplicationAdapter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateRecord(store, type, snapshot) {
|
||||||
|
const { backend, issuerId } = snapshot.record;
|
||||||
|
const data = this.serialize(snapshot);
|
||||||
|
const url = this.urlForQuery(backend, issuerId);
|
||||||
|
return this.ajax(url, 'POST', { data });
|
||||||
|
}
|
||||||
|
|
||||||
query(store, type, query) {
|
query(store, type, query) {
|
||||||
return this.ajax(this.urlForQuery(query.backend), 'GET', this.optionsForQuery());
|
return this.ajax(this.urlForQuery(query.backend), 'GET', this.optionsForQuery());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,31 @@ import { attr } from '@ember-data/model';
|
||||||
import { withFormFields } from 'vault/decorators/model-form-fields';
|
import { withFormFields } from 'vault/decorators/model-form-fields';
|
||||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||||
|
|
||||||
@withFormFields(null, [
|
const issuerUrls = ['issuingCertificates', 'crlDistributionPoints', 'ocspServers'];
|
||||||
{
|
@withFormFields(
|
||||||
default: [
|
['issuerName', 'leafNotAfterBehavior', 'usage', 'manualChain', ...issuerUrls],
|
||||||
'certificate',
|
[
|
||||||
'caChain',
|
{
|
||||||
'commonName',
|
default: [
|
||||||
'issuerName',
|
'certificate',
|
||||||
'notValidBefore',
|
'caChain',
|
||||||
'serialNumber',
|
'commonName',
|
||||||
'keyId',
|
'issuerName',
|
||||||
'uriSans',
|
'notValidBefore',
|
||||||
'notValidAfter',
|
'serialNumber',
|
||||||
],
|
'keyId',
|
||||||
},
|
'uriSans',
|
||||||
{ 'Issuer URLs': ['issuingCertificates', 'crlDistributionPoints', 'ocspServers', 'deltaCrlUrls'] },
|
'notValidAfter',
|
||||||
])
|
],
|
||||||
|
},
|
||||||
|
{ 'Issuer URLs': issuerUrls },
|
||||||
|
]
|
||||||
|
)
|
||||||
export default class PkiIssuerModel extends PkiCertificateBaseModel {
|
export default class PkiIssuerModel extends PkiCertificateBaseModel {
|
||||||
getHelpUrl(backend) {
|
// there are too many differences between what openAPI returns and the designs for the update form
|
||||||
return `/v1/${backend}/issuer/example?help=1`;
|
// manually defining the attrs instead with the correct meta data
|
||||||
|
get useOpenAPI() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@attr('string') issuerId;
|
@attr('string') issuerId;
|
||||||
|
@ -42,6 +48,57 @@ export default class PkiIssuerModel extends PkiCertificateBaseModel {
|
||||||
})
|
})
|
||||||
uriSans;
|
uriSans;
|
||||||
|
|
||||||
|
@attr('string') issuerName;
|
||||||
|
|
||||||
|
@attr({
|
||||||
|
label: 'Leaf notAfter behavior',
|
||||||
|
subText:
|
||||||
|
'What happens when a leaf certificate is issued, but its NotAfter field (and therefore its expiry date) exceeds that of this issuer.',
|
||||||
|
docLink: '/vault/api-docs/secret/pki#update-issuer',
|
||||||
|
editType: 'yield',
|
||||||
|
valueOptions: ['err', 'truncate', 'permit'],
|
||||||
|
})
|
||||||
|
leafNotAfterBehavior;
|
||||||
|
|
||||||
|
@attr({
|
||||||
|
label: 'Usage',
|
||||||
|
subText: 'Allowed usages for this issuer. It can always be read',
|
||||||
|
editType: 'yield',
|
||||||
|
valueOptions: [
|
||||||
|
{ label: 'Issuing certificates', value: 'issuing-certificates' },
|
||||||
|
{ label: 'Signing CRLs', value: 'crl-signing' },
|
||||||
|
{ label: 'Signing OCSPs', value: 'ocsp-signing' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
usage;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
label: 'Manual chain',
|
||||||
|
subText:
|
||||||
|
"An advanced field useful when automatic chain building isn't desired. The first element must be the present issuer's reference.",
|
||||||
|
})
|
||||||
|
manualChain;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
label: 'Issuing certificates',
|
||||||
|
subText:
|
||||||
|
'The URL values for the Issuing Certificate field. These are different URLs for the same resource, and should be added individually, not in a comma-separated list.',
|
||||||
|
editType: 'stringArray',
|
||||||
|
})
|
||||||
|
issuingCertificates;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
label: 'CRL distribution points',
|
||||||
|
subText: 'Specifies the URL values for the CRL Distribution Points field.',
|
||||||
|
})
|
||||||
|
crlDistributionPoints;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
label: 'OCSP servers',
|
||||||
|
subText: 'Specifies the URL values for the OCSP Servers field.',
|
||||||
|
})
|
||||||
|
ocspServers;
|
||||||
|
|
||||||
@lazyCapabilities(apiPath`${'backend'}/issuer/${'issuerId'}`) issuerPath;
|
@lazyCapabilities(apiPath`${'backend'}/issuer/${'issuerId'}`) issuerPath;
|
||||||
@lazyCapabilities(apiPath`${'backend'}/root/rotate/exported`) rotateExported;
|
@lazyCapabilities(apiPath`${'backend'}/root/rotate/exported`) rotateExported;
|
||||||
@lazyCapabilities(apiPath`${'backend'}/root/rotate/internal`) rotateInternal;
|
@lazyCapabilities(apiPath`${'backend'}/root/rotate/internal`) rotateInternal;
|
||||||
|
|
|
@ -4,6 +4,25 @@ import ApplicationSerializer from '../application';
|
||||||
export default class PkiIssuerSerializer extends ApplicationSerializer {
|
export default class PkiIssuerSerializer extends ApplicationSerializer {
|
||||||
primaryKey = 'issuer_id';
|
primaryKey = 'issuer_id';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
// remove following attrs from serialization
|
||||||
|
const attrs = [
|
||||||
|
'caChain',
|
||||||
|
'certificate',
|
||||||
|
'commonName',
|
||||||
|
'issuerId',
|
||||||
|
'keyId',
|
||||||
|
'notValidAfter',
|
||||||
|
'notValidBefore',
|
||||||
|
'serialNumber',
|
||||||
|
];
|
||||||
|
this.attrs = attrs.reduce((attrObj, attr) => {
|
||||||
|
attrObj[attr] = { serialize: false };
|
||||||
|
return attrObj;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||||
if (payload.data.certificate) {
|
if (payload.data.certificate) {
|
||||||
// Parse certificate back from the API and add to payload
|
// Parse certificate back from the API and add to payload
|
||||||
|
|
77
ui/lib/pki/addon/components/page/pki-issuer-edit.hbs
Normal file
77
ui/lib/pki/addon/components/page/pki-issuer-edit.hbs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<form {{on "submit" (perform this.save)}}>
|
||||||
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
|
<MessageError @errorMessage={{this.error}} />
|
||||||
|
{{#each @model.formFields as |field|}}
|
||||||
|
{{#if (eq field.name "issuingCertificates")}}
|
||||||
|
<div class="has-top-margin-m has-bottom-margin-s">
|
||||||
|
<h2 class="title is-4">
|
||||||
|
Issuer URLs
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<FormField @attr={{field}} @model={{@model}}>
|
||||||
|
{{#if (eq field.name "usage")}}
|
||||||
|
{{#each field.options.valueOptions as |option|}}
|
||||||
|
<div class="is-flex-center has-text-grey has-text-weight-bold">
|
||||||
|
<Input
|
||||||
|
data-test-usage={{option.label}}
|
||||||
|
id={{option.value}}
|
||||||
|
@type="checkbox"
|
||||||
|
@checked={{includes option.value this.usageValues}}
|
||||||
|
{{on "change" (fn this.setUsage option.value)}}
|
||||||
|
/>
|
||||||
|
<label for={{option.value}} class="has-left-margin-s">{{option.label}}</label>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{else if (eq field.name "leafNotAfterBehavior")}}
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<div class="select is-fullwidth">
|
||||||
|
<select
|
||||||
|
name={{@attr.name}}
|
||||||
|
id={{@attr.name}}
|
||||||
|
{{on "change" (pipe (pick "target.value") (fn (mut @model.leafNotAfterBehavior)))}}
|
||||||
|
onchange={{this.onChangeWithEvent}}
|
||||||
|
>
|
||||||
|
{{#each field.options.valueOptions as |value|}}
|
||||||
|
<option selected={{eq @model.leafNotAfterBehavior value}} value={{value}}>
|
||||||
|
{{capitalize (if (eq value "err") "error" value)}}
|
||||||
|
if the computed NotAfter exceeds that of this issuer
|
||||||
|
</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</FormField>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<div class="has-top-margin-l has-bottom-margin-l">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="button is-primary {{if this.save.isRunning 'is-loading'}}"
|
||||||
|
disabled={{this.save.isRunning}}
|
||||||
|
data-test-save
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button has-left-margin-s"
|
||||||
|
disabled={{this.save.isRunning}}
|
||||||
|
{{on "click" this.cancel}}
|
||||||
|
data-test-cancel
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
{{#if this.error}}
|
||||||
|
<div class="control">
|
||||||
|
<AlertInline
|
||||||
|
@type="danger"
|
||||||
|
@paddingTop={{true}}
|
||||||
|
@message="There was an error submitting this form."
|
||||||
|
@mimicRefresh={{true}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</form>
|
57
ui/lib/pki/addon/components/page/pki-issuer-edit.ts
Normal file
57
ui/lib/pki/addon/components/page/pki-issuer-edit.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
import { inject as 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 RouterService from '@ember/routing/router-service';
|
||||||
|
import FlashMessageService from 'vault/services/flash-messages';
|
||||||
|
import PkiIssuerModel from 'vault/models/pki/issuer';
|
||||||
|
|
||||||
|
interface Args {
|
||||||
|
model: PkiIssuerModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PkiIssuerEditComponent extends Component<Args> {
|
||||||
|
@service declare readonly router: RouterService;
|
||||||
|
@service declare readonly flashMessages: FlashMessageService;
|
||||||
|
|
||||||
|
@tracked usageValues: Array<string> = [];
|
||||||
|
@tracked error = null;
|
||||||
|
|
||||||
|
constructor(owner: unknown, args: Args) {
|
||||||
|
super(owner, args);
|
||||||
|
this.usageValues = (this.args.model.usage || '').split(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
toDetails() {
|
||||||
|
this.router.transitionTo('vault.cluster.secrets.backend.pki.issuers.issuer.details');
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
setUsage(value: string) {
|
||||||
|
const method = this.usageValues.includes(value) ? 'removeObject' : 'addObject';
|
||||||
|
this.usageValues[method](value);
|
||||||
|
this.args.model.usage = this.usageValues.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
@task
|
||||||
|
@waitFor
|
||||||
|
*save(event: Event) {
|
||||||
|
event.preventDefault();
|
||||||
|
try {
|
||||||
|
yield this.args.model.save();
|
||||||
|
this.flashMessages.success('Successfully updated issuer');
|
||||||
|
this.toDetails();
|
||||||
|
} catch (error) {
|
||||||
|
this.error = errorMessage(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
cancel() {
|
||||||
|
this.args.model.rollbackAttributes();
|
||||||
|
this.toDetails();
|
||||||
|
}
|
||||||
|
}
|
10
ui/lib/pki/addon/routes/index.js
Normal file
10
ui/lib/pki/addon/routes/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class PkiRoute extends Route {
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
redirect() {
|
||||||
|
this.router.transitionTo('vault.cluster.secrets.backend.pki.overview');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
import Route from '@ember/routing/route';
|
import PkiIssuerDetailsRoute from './details';
|
||||||
|
|
||||||
export default class PkiIssuerEditRoute extends Route {}
|
export default class PkiIssuerEditRoute extends PkiIssuerDetailsRoute {}
|
||||||
|
|
|
@ -1 +1,12 @@
|
||||||
route: issuers.issuer.edit
|
<PageHeader as |p|>
|
||||||
|
<p.top>
|
||||||
|
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||||
|
</p.top>
|
||||||
|
<p.levelLeft>
|
||||||
|
<h1 class="title is-3" data-test-pki-issuer-page-title>
|
||||||
|
Update issuer
|
||||||
|
</h1>
|
||||||
|
</p.levelLeft>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
|
<Page::PkiIssuerEdit @model={{this.model}} />
|
|
@ -30,6 +30,7 @@
|
||||||
"test:oss": "yarn run test -f='!enterprise'",
|
"test:oss": "yarn run test -f='!enterprise'",
|
||||||
"test:quick": "node scripts/start-vault.js",
|
"test:quick": "node scripts/start-vault.js",
|
||||||
"test:quick-oss": "yarn test:quick -f='!enterprise'",
|
"test:quick-oss": "yarn test:quick -f='!enterprise'",
|
||||||
|
"types:declare": "declare () { yarn tsc $1 --declaration --allowJs --emitDeclarationOnly --experimentalDecorators --outDir $2; }; declare",
|
||||||
"vault": "VAULT_REDIRECT_ADDR=http://127.0.0.1:8200 vault server -log-level=error -dev -dev-root-token-id=root -dev-ha -dev-transactional",
|
"vault": "VAULT_REDIRECT_ADDR=http://127.0.0.1:8200 vault server -log-level=error -dev -dev-root-token-id=root -dev-ha -dev-transactional",
|
||||||
"vault:cluster": "VAULT_REDIRECT_ADDR=http://127.0.0.1:8202 vault server -log-level=error -dev -dev-root-token-id=root -dev-listen-address=127.0.0.1:8202 -dev-ha -dev-transactional"
|
"vault:cluster": "VAULT_REDIRECT_ADDR=http://127.0.0.1:8202 vault server -log-level=error -dev -dev-root-token-id=root -dev-listen-address=127.0.0.1:8202 -dev-ha -dev-transactional"
|
||||||
},
|
},
|
||||||
|
|
|
@ -383,7 +383,7 @@ module('Acceptance | pki workflow', function (hooks) {
|
||||||
.exists({ count: 9 }, 'Renders 9 info table items under default group');
|
.exists({ count: 9 }, 'Renders 9 info table items under default group');
|
||||||
assert
|
assert
|
||||||
.dom(`${SELECTORS.issuerDetails.urlsGroup} ${SELECTORS.issuerDetails.row}`)
|
.dom(`${SELECTORS.issuerDetails.urlsGroup} ${SELECTORS.issuerDetails.row}`)
|
||||||
.exists({ count: 4 }, 'Renders 4 info table items under URLs group');
|
.exists({ count: 3 }, 'Renders 4 info table items under URLs group');
|
||||||
assert.dom(SELECTORS.issuerDetails.groupTitle).exists({ count: 1 }, 'only 1 group title rendered');
|
assert.dom(SELECTORS.issuerDetails.groupTitle).exists({ count: 1 }, 'only 1 group title rendered');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
145
ui/tests/integration/components/pki/page/pki-issuer-edit-test.js
Normal file
145
ui/tests/integration/components/pki/page/pki-issuer-edit-test.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
|
import { click, fillIn, 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 { Response } from 'miragejs';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
const selectors = {
|
||||||
|
name: '[data-test-input="issuerName"]',
|
||||||
|
leaf: '[data-test-field="leafNotAfterBehavior"] select',
|
||||||
|
leafOption: '[data-test-field="leafNotAfterBehavior"] option',
|
||||||
|
usageCert: '[data-test-usage="Issuing certificates"]',
|
||||||
|
usageCrl: '[data-test-usage="Signing CRLs"]',
|
||||||
|
usageOcsp: '[data-test-usage="Signing OCSPs"]',
|
||||||
|
manualChain: '[data-test-input="manualChain"]',
|
||||||
|
certUrls: '[data-test-string-list-input]',
|
||||||
|
certUrl1: '[data-test-string-list-input="0"]',
|
||||||
|
certUrl2: '[data-test-string-list-input="1"]',
|
||||||
|
certUrlAdd: '[data-test-string-list-button="add"]',
|
||||||
|
certUrlRemove: '[data-test-string-list-button="delete"]',
|
||||||
|
crlDist: '[data-test-input="crlDistributionPoints"]',
|
||||||
|
ocspServers: '[data-test-input="ocspServers"]',
|
||||||
|
save: '[data-test-save]',
|
||||||
|
cancel: '[data-test-cancel]',
|
||||||
|
error: '[data-test-error] p',
|
||||||
|
alert: '[data-test-inline-error-message]',
|
||||||
|
};
|
||||||
|
|
||||||
|
module('Integration | Component | pki | Page::PkiIssuerEditPage::PkiIssuerEdit', function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
setupEngine(hooks, 'pki');
|
||||||
|
setupMirage(hooks);
|
||||||
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
const router = this.owner.lookup('service:router');
|
||||||
|
const transitionSpy = sinon.stub(router, 'transitionTo');
|
||||||
|
this.transitionCalled = () =>
|
||||||
|
transitionSpy.calledWith('vault.cluster.secrets.backend.pki.issuers.issuer.details');
|
||||||
|
|
||||||
|
const store = this.owner.lookup('service:store');
|
||||||
|
store.pushPayload('pki/issuer', {
|
||||||
|
modelName: 'pki/issuer',
|
||||||
|
data: {
|
||||||
|
issuer_id: 'test',
|
||||||
|
issuer_name: 'foo-bar',
|
||||||
|
leaf_not_after_behavior: 'err',
|
||||||
|
usage: 'read-only,issuing-certificates,ocsp-signing',
|
||||||
|
manual_chain: 'issuer_ref',
|
||||||
|
issuing_certificates: ['http://localhost', 'http://localhost:8200'],
|
||||||
|
crl_distribution_points: 'http://localhost',
|
||||||
|
ocsp_servers: 'http://localhost',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.model = store.peekRecord('pki/issuer', 'test');
|
||||||
|
// backend value on model pulled from secretMountPath service
|
||||||
|
this.owner.lookup('service:secretMountPath').update('pki');
|
||||||
|
|
||||||
|
this.update = async () => {
|
||||||
|
await fillIn(selectors.name, 'bar-baz');
|
||||||
|
await click(selectors.usageCrl);
|
||||||
|
await click(selectors.certUrlRemove);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should populate fields with model values', async function (assert) {
|
||||||
|
await render(hbs`<Page::PkiIssuerEdit @model={{this.model}} />`, { owner: this.engine });
|
||||||
|
|
||||||
|
assert.dom(selectors.name).hasValue(this.model.issuerName, 'Issuer name field populates');
|
||||||
|
assert
|
||||||
|
.dom(selectors.leaf)
|
||||||
|
.hasValue(this.model.leafNotAfterBehavior, 'Leaf not after behavior option selected');
|
||||||
|
assert
|
||||||
|
.dom(selectors.leafOption)
|
||||||
|
.hasText(
|
||||||
|
'Error if the computed NotAfter exceeds that of this issuer',
|
||||||
|
'Correct text renders for leaf option'
|
||||||
|
);
|
||||||
|
assert.dom(selectors.usageCert).isChecked('Usage issuing certificates is checked');
|
||||||
|
assert.dom(selectors.usageCrl).isNotChecked('Usage signing crls is not checked');
|
||||||
|
assert.dom(selectors.usageOcsp).isChecked('Usage signing ocsps is checked');
|
||||||
|
assert.dom(selectors.manualChain).hasValue(this.model.manualChain, 'Manual chain field populates');
|
||||||
|
const certUrls = this.model.issuingCertificates.split(',');
|
||||||
|
assert.dom(selectors.certUrl1).hasValue(certUrls[0], 'Issuing certificate populates');
|
||||||
|
assert.dom(selectors.certUrl2).hasValue(certUrls[1], 'Issuing certificate populates');
|
||||||
|
assert
|
||||||
|
.dom(selectors.crlDist)
|
||||||
|
.hasValue(this.model.crlDistributionPoints, 'Crl distribution points populate');
|
||||||
|
assert.dom(selectors.ocspServers).hasValue(this.model.ocspServers, 'Ocsp servers populate');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should rollback model attributes on cancel', async function (assert) {
|
||||||
|
await render(hbs`<Page::PkiIssuerEdit @model={{this.model}} />`, { owner: this.engine });
|
||||||
|
|
||||||
|
await this.update();
|
||||||
|
await click(selectors.cancel);
|
||||||
|
|
||||||
|
assert.ok(this.transitionCalled(), 'Transitions to details route on cancel');
|
||||||
|
assert.strictEqual(this.model.issuerName, 'foo-bar', 'Issuer name rolled back');
|
||||||
|
assert.strictEqual(this.model.usage, 'read-only,issuing-certificates,ocsp-signing', 'Usage rolled back');
|
||||||
|
assert.strictEqual(
|
||||||
|
this.model.issuingCertificates,
|
||||||
|
'http://localhost,http://localhost:8200',
|
||||||
|
'Issuing certificates rolled back'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should update issuer', async function (assert) {
|
||||||
|
assert.expect(4);
|
||||||
|
|
||||||
|
this.server.post('/pki/issuer/test', (schema, req) => {
|
||||||
|
const data = JSON.parse(req.requestBody);
|
||||||
|
assert.strictEqual(data.issuer_name, 'bar-baz', 'Updated issuer name sent in POST request');
|
||||||
|
assert.strictEqual(
|
||||||
|
data.usage,
|
||||||
|
'read-only,issuing-certificates,ocsp-signing,crl-signing',
|
||||||
|
'Updated usage sent in POST request'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
data.issuing_certificates,
|
||||||
|
'http://localhost:8200',
|
||||||
|
'Updated issuing certificates sent in POST request'
|
||||||
|
);
|
||||||
|
return { data };
|
||||||
|
});
|
||||||
|
await render(hbs`<Page::PkiIssuerEdit @model={{this.model}} />`, { owner: this.engine });
|
||||||
|
|
||||||
|
await this.update();
|
||||||
|
await click(selectors.save);
|
||||||
|
assert.ok(this.transitionCalled(), 'Transitions to details route on save success');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should show error messages', async function (assert) {
|
||||||
|
this.server.post('/pki/issuer/test', () => new Response(404, {}, { errors: ['Some error occurred'] }));
|
||||||
|
|
||||||
|
await render(hbs`<Page::PkiIssuerEdit @model={{this.model}} />`, { owner: this.engine });
|
||||||
|
await click(selectors.save);
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom(selectors.alert)
|
||||||
|
.hasText('There was an error submitting this form.', 'Inline error alert renders');
|
||||||
|
assert.dom(selectors.error).hasText('Some error occurred', 'Error message renders');
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,3 +4,7 @@ export interface FormField {
|
||||||
type: string;
|
type: string;
|
||||||
options: unknown;
|
options: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FormFieldGroups {
|
||||||
|
[key: string]: Array<FormField>;
|
||||||
|
}
|
||||||
|
|
29
ui/types/vault/models/pki/issuer.d.ts
vendored
Normal file
29
ui/types/vault/models/pki/issuer.d.ts
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import PkiCertificateBaseModel from './certificate/base';
|
||||||
|
import { FormField, FormFieldGroups } from 'vault/app-types';
|
||||||
|
|
||||||
|
export default class PkiIssuerModel extends PkiCertificateBaseModel {
|
||||||
|
useOpenAPI(): boolean;
|
||||||
|
issuerId: string;
|
||||||
|
keyId: string;
|
||||||
|
uriSans: string;
|
||||||
|
issuerName: string;
|
||||||
|
leafNotAfterBehavior: string;
|
||||||
|
usage: string;
|
||||||
|
manualChain: string;
|
||||||
|
issuingCertificates: string;
|
||||||
|
crlDistributionPoints: string;
|
||||||
|
ocspServers: string;
|
||||||
|
/** these are all instances of the capabilities model which should be converted to native class and typed
|
||||||
|
rotateExported: any;
|
||||||
|
rotateInternal: any;
|
||||||
|
rotateExisting: any;
|
||||||
|
crossSignPath: any;
|
||||||
|
signIntermediate: any;
|
||||||
|
-------------------- **/
|
||||||
|
formFields: Array<FormField>;
|
||||||
|
formFieldGroups: FormFieldGroups;
|
||||||
|
get canRotateIssuer(): boolean;
|
||||||
|
get canCrossSign(): boolean;
|
||||||
|
get canSignIntermediate(): boolean;
|
||||||
|
get canConfigure(): boolean;
|
||||||
|
}
|
Loading…
Reference in a new issue