UI: pki configuration edit form (#20245)
* setup routing, move queries in ConfigurationIndex to parent resource route * finish building out form, add model attrs build ttls * add types * update model attribute values, fix default ttl states * remove defaults and use openApi, group with booleans * add model to application route" * add save functionality * add error banner * add transition after save * use defaults from open api * fix empty state language * pass engine data * change model attrs to ttl objects * update types * add invalid form alert to error block * move data manipulation to serialize * fix serializer, add comments * add test for serializer * edit configuration details view * update details test * change to updateRecord so POST request is made * config/urls use POST instead of PUT * add edit tests, update details * add model hooks back to routes * rearrange to remove dif * remove createRecord for urls * update comment * wip sample ttl transform * Revert "wip sample ttl transform" This reverts commit 59fc179b5cd2994c4258e553e56667e29b3d6b72. * revert changes, move model updates back to component * simplify model fetches * address comments; * update pki/urls test * update adapter test
This commit is contained in:
parent
ad18fc6398
commit
76f1971126
|
@ -18,4 +18,9 @@ export default class PkiCrlAdapter extends ApplicationAdapter {
|
|||
return resp.data;
|
||||
});
|
||||
}
|
||||
|
||||
updateRecord(store, type, snapshot) {
|
||||
const data = snapshot.serialize();
|
||||
return this.ajax(this._url(snapshot.record.id), 'POST', { data });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,12 @@ export default class PkiUrlsAdapter extends ApplicationAdapter {
|
|||
return `${this.buildURL()}/${encodePath(backend)}/config/urls`;
|
||||
}
|
||||
|
||||
urlForCreateRecord(modelName, snapshot) {
|
||||
return this._url(snapshot.record.id);
|
||||
updateRecord(store, type, snapshot) {
|
||||
const data = snapshot.serialize();
|
||||
return this.ajax(this._url(snapshot.record.id), 'POST', { data });
|
||||
}
|
||||
|
||||
urlForFindRecord(id) {
|
||||
return this._url(id);
|
||||
}
|
||||
urlForUpdateRecord(store, type, snapshot) {
|
||||
return this._url(snapshot.record.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,67 @@
|
|||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { withFormFields } from 'vault/decorators/model-form-fields';
|
||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
|
||||
@withFormFields(['expiry', 'autoRebuildGracePeriod', 'deltaRebuildInterval', 'ocspExpiry'])
|
||||
export default class PkiCrlModel extends Model {
|
||||
// This model uses the backend value as the model ID
|
||||
get useOpenAPI() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@attr('string') expiry;
|
||||
@attr('boolean') autoRebuild;
|
||||
@attr('string') ocspExpiry;
|
||||
@attr('string', {
|
||||
label: 'Auto-rebuild on',
|
||||
labelDisabled: 'Auto-rebuild off',
|
||||
mapToBoolean: 'autoRebuild',
|
||||
isOppositeValue: false,
|
||||
helperTextEnabled: 'Vault will rebuild the CRL in the below grace period before expiration',
|
||||
helperTextDisabled: 'Vault will not automatically rebuild the CRL',
|
||||
})
|
||||
autoRebuildGracePeriod;
|
||||
|
||||
@attr('boolean') enableDelta;
|
||||
@attr('string', {
|
||||
label: 'Delta CRL building on',
|
||||
labelDisabled: 'Delta CRL building off',
|
||||
mapToBoolean: 'enableDelta',
|
||||
isOppositeValue: false,
|
||||
helperTextEnabled: 'Vault will rebuild the delta CRL at the interval below:',
|
||||
helperTextDisabled: 'Vault will not rebuild the delta CRL at an interval',
|
||||
})
|
||||
deltaRebuildInterval;
|
||||
|
||||
@attr('boolean') disable;
|
||||
@attr('string', {
|
||||
label: 'Expiry',
|
||||
labelDisabled: 'No expiry',
|
||||
mapToBoolean: 'disable',
|
||||
isOppositeValue: true,
|
||||
helperTextDisabled: 'The CRL will not be built.',
|
||||
helperTextEnabled: 'The CRL will expire after:',
|
||||
})
|
||||
expiry;
|
||||
|
||||
@attr('boolean') ocspDisable;
|
||||
@attr('string', {
|
||||
label: 'OCSP responder APIs enabled',
|
||||
labelDisabled: 'OCSP responder APIs disabled',
|
||||
mapToBoolean: 'ocspDisable',
|
||||
isOppositeValue: true,
|
||||
helperTextEnabled: "Requests about a certificate's status will be valid for:",
|
||||
helperTextDisabled: 'Requests cannot be made to check if an individual certificate is valid.',
|
||||
})
|
||||
ocspExpiry;
|
||||
|
||||
// TODO follow-on ticket to add enterprise only attributes:
|
||||
/*
|
||||
@attr('boolean') crossClusterRevocation;
|
||||
@attr('boolean') unifiedCrl;
|
||||
@attr('boolean') unifiedCrlOnExistingPaths;
|
||||
*/
|
||||
|
||||
@lazyCapabilities(apiPath`${'id'}/config/crl`, 'id') crlPath;
|
||||
|
||||
get canSet() {
|
||||
return this.crlPath.get('canCreate') !== false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* @param onChange {Function} - This function will be passed a TTL object, which includes enabled{bool}, seconds{number}, timeString{string}, goSafeTimeString{string}.
|
||||
* @param initialEnabled=false {Boolean} - Set this value if you want the toggle on when component is mounted
|
||||
* @param label="Time to live (TTL)" {String} - Label is the main label that lives next to the toggle. Yielded values will replace the label
|
||||
* @param labelDisabled=Label to display when TTL is toggled off
|
||||
* @param helperTextEnabled="" {String} - This helper text is shown under the label when the toggle is switched on
|
||||
* @param helperTextDisabled="" {String} - This helper text is shown under the label when the toggle is switched off
|
||||
* @param initialValue=null {string} - InitialValue is the duration value which will be shown when the component is loaded. If it can't be parsed, will default to 0.
|
||||
|
@ -52,6 +53,9 @@ export default class TtlPickerComponent extends Component {
|
|||
elementId = 'ttl-' + guidFor(this);
|
||||
|
||||
get label() {
|
||||
if (this.args.label && this.args.labelDisabled) {
|
||||
return this.enableTTL ? this.args.label : this.args.labelDisabled;
|
||||
}
|
||||
return this.args.label || 'Time to live (TTL)';
|
||||
}
|
||||
get helperText() {
|
||||
|
|
|
@ -38,14 +38,37 @@
|
|||
<h2 class="title is-4 has-bottom-margin-xs has-top-margin-xl has-border-bottom-light has-bottom-padding-s">
|
||||
Certificate Revocation List (CRL)
|
||||
</h2>
|
||||
<InfoTableRow @label="Expiry" @value={{@crl.expiry}} />
|
||||
<InfoTableRow @label="Auto-rebuild" @value={{if @crl.autoRebuild "On" "Off"}} />
|
||||
|
||||
<InfoTableRow @label="CRL building" @value={{if @crl.disable "Disabled" "Enabled"}} />
|
||||
{{#unless @crl.disable}}
|
||||
<InfoTableRow @label="Expiry" @value={{@crl.expiry}} />
|
||||
<InfoTableRow @label="Auto-rebuild">
|
||||
<Icon
|
||||
class={{if @crl.autoRebuild "icon-true" "icon-false"}}
|
||||
@name={{if @crl.autoRebuild "check-circle" "x-square"}}
|
||||
/>
|
||||
{{if @crl.autoRebuild "On" "Off"}}
|
||||
</InfoTableRow>
|
||||
{{#if @crl.autoRebuild}}
|
||||
<InfoTableRow @label="Auto-rebuild grace period" @value={{@crl.autoRebuildGracePeriod}} />
|
||||
{{/if}}
|
||||
<InfoTableRow @label="Delta CRL building">
|
||||
<Icon
|
||||
class={{if @crl.enableDelta "icon-true" "icon-false"}}
|
||||
@name={{if @crl.enableDelta "check-circle" "x-square"}}
|
||||
/>
|
||||
{{if @crl.enableDelta "On" "Off"}}
|
||||
</InfoTableRow>
|
||||
{{#if @crl.enableDelta}}
|
||||
<InfoTableRow @label="Delta rebuild interval" @value={{@crl.deltaRebuildInterval}} />
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
<h2 class="title is-4 has-bottom-margin-xs has-top-margin-xl has-border-bottom-light has-bottom-padding-s">
|
||||
Online Certificate Status Protocol (OCSP)
|
||||
</h2>
|
||||
<InfoTableRow @label="Responder APIs" @value={{if @crl.ocspDisable "Disabled" "Enabled"}} />
|
||||
<InfoTableRow @label="Interval" @value={{@crl.ocspExpiry}} />
|
||||
{{#unless @crl.ocspDisable}}
|
||||
<InfoTableRow @label="Interval" @value={{@crl.ocspExpiry}} />
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<Toolbar>
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{#if this.errorBanner}}
|
||||
<AlertBanner @type="danger" @message={{this.errorBanner}} data-test-error-banner />
|
||||
{{/if}}
|
||||
<form {{on "submit" (perform this.save)}}>
|
||||
<fieldset class="box is-shadowless is-marginless is-borderless is-fullwidth" data-test-urls-edit-section>
|
||||
<h2 class="title is-size-5 has-border-bottom-light page-header">
|
||||
Global URLs
|
||||
</h2>
|
||||
{{#if @urls.canSet}}
|
||||
{{#each @urls.allFields as |attr|}}
|
||||
<FormField @attr={{attr}} @model={{@urls}} @showHelpText={{false}} />
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<EmptyState
|
||||
class="is-box-shadowless"
|
||||
@title="You do not have permission to set URLs"
|
||||
@message="Ask your administrator if you think you should have access to:"
|
||||
>
|
||||
<code>POST /{{@backend}}/config/urls</code>
|
||||
</EmptyState>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="box is-shadowless is-marginless is-borderless is-fullwidth" data-test-crl-edit-section>
|
||||
<h2 class="title is-size-5 has-border-bottom-light page-header">
|
||||
Certificate Revocation List (CRL)
|
||||
</h2>
|
||||
{{#if @crl.canSet}}
|
||||
{{#each @crl.formFields as |attr|}}
|
||||
{{#if (eq attr.name "ocspExpiry")}}
|
||||
<h2 class="title is-size-5 has-border-bottom-light page-header">
|
||||
Online Certificate Status Protocol (OCSP)
|
||||
</h2>
|
||||
{{/if}}
|
||||
{{#if (or (includes attr.name this.alwaysRender) (not @crl.disable))}}
|
||||
{{#let (get @crl attr.options.mapToBoolean) as |booleanValue|}}
|
||||
<div class="field">
|
||||
<TtlPicker
|
||||
data-test-input={{attr.name}}
|
||||
@onChange={{fn this.handleTtl attr}}
|
||||
@label={{attr.options.label}}
|
||||
@labelDisabled={{attr.options.labelDisabled}}
|
||||
@helperTextDisabled={{attr.options.helperTextDisabled}}
|
||||
@helperTextEnabled={{attr.options.helperTextEnabled}}
|
||||
@initialEnabled={{if attr.options.isOppositeValue (not booleanValue) booleanValue}}
|
||||
@initialValue={{get @crl attr.name}}
|
||||
/>
|
||||
</div>
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<EmptyState
|
||||
class="is-box-shadowless"
|
||||
@title="You do not have permission to set revocation configuration"
|
||||
@message="Ask your administrator if you think you should have access to:"
|
||||
>
|
||||
<code>POST /{{@backend}}/config/crl</code>
|
||||
</EmptyState>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
{{#if (or @urls.canSet @crl.canSet)}}
|
||||
<button
|
||||
type="submit"
|
||||
class="button is-primary {{if this.save.isRunning 'is-loading'}}"
|
||||
disabled={{this.save.isRunning}}
|
||||
data-test-configuration-edit-save
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
{{/if}}
|
||||
<button
|
||||
{{on "click" this.cancel}}
|
||||
type="button"
|
||||
class="button has-left-margin-s"
|
||||
disabled={{this.save.isRunning}}
|
||||
data-test-configuration-edit-cancel
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
{{#if this.invalidFormAlert}}
|
||||
<div class="control">
|
||||
<AlertInline
|
||||
@type="danger"
|
||||
@paddingTop={{true}}
|
||||
@message={{this.invalidFormAlert}}
|
||||
@mimicRefresh={{true}}
|
||||
data-test-configuration-edit-validation-alert
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
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 RouterService from '@ember/routing/router-service';
|
||||
import FlashMessageService from 'vault/services/flash-messages';
|
||||
import { FormField, TtlEvent } from 'vault/app-types';
|
||||
import PkiCrlModel from 'vault/models/pki/crl';
|
||||
import PkiUrlsModel from 'vault/models/pki/urls';
|
||||
import errorMessage from 'vault/utils/error-message';
|
||||
|
||||
interface Args {
|
||||
crl: PkiCrlModel;
|
||||
urls: PkiUrlsModel;
|
||||
}
|
||||
interface PkiCrlTtls {
|
||||
autoRebuildGracePeriod: string;
|
||||
expiry: string;
|
||||
deltaRebuildInterval: string;
|
||||
ocspExpiry: string;
|
||||
}
|
||||
interface PkiCrlBooleans {
|
||||
autoRebuild: boolean;
|
||||
enableDelta: boolean;
|
||||
disable: boolean;
|
||||
ocspDisable: boolean;
|
||||
}
|
||||
export default class PkiConfigurationEditComponent extends Component<Args> {
|
||||
@service declare readonly router: RouterService;
|
||||
@service declare readonly flashMessages: FlashMessageService;
|
||||
|
||||
@tracked invalidFormAlert = '';
|
||||
@tracked errorBanner = '';
|
||||
|
||||
get alwaysRender() {
|
||||
return ['expiry', 'ocspExpiry'];
|
||||
}
|
||||
|
||||
@task
|
||||
@waitFor
|
||||
*save(event: Event) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
yield this.args.urls.save();
|
||||
yield this.args.crl.save();
|
||||
this.flashMessages.success('Successfully updated configuration');
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.pki.configuration.index');
|
||||
} catch (error) {
|
||||
this.invalidFormAlert = 'There was an error submitting this form.';
|
||||
this.errorBanner = errorMessage(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
cancel() {
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.pki.configuration.index');
|
||||
}
|
||||
|
||||
@action
|
||||
handleTtl(attr: FormField, e: TtlEvent) {
|
||||
const { enabled, goSafeTimeString } = e;
|
||||
const ttlAttr = attr.name;
|
||||
this.args.crl[ttlAttr as keyof PkiCrlTtls] = goSafeTimeString;
|
||||
// expiry and ocspExpiry both correspond to 'disable' booleans
|
||||
// so when ttl is enabled, the booleans are set to false
|
||||
this.args.crl[attr.options.mapToBoolean as keyof PkiCrlBooleans] = attr.options.isOppositeValue
|
||||
? !enabled
|
||||
: enabled;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* the overview, roles, issuers, certificates, and key routes all need to be aware of the whether there is a config for the engine
|
||||
|
@ -21,6 +22,7 @@ export function withConfig() {
|
|||
return SuperClass;
|
||||
}
|
||||
return class CheckConfig extends SuperClass {
|
||||
@service secretMountPath;
|
||||
shouldPromptConfig = false;
|
||||
|
||||
async beforeModel() {
|
||||
|
|
|
@ -4,5 +4,18 @@
|
|||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { hash } from 'rsvp';
|
||||
|
||||
export default class PkiConfigurationRoute extends Route {}
|
||||
export default class PkiConfigurationRoute extends Route {
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
const engine = this.modelFor('application');
|
||||
return hash({
|
||||
engine,
|
||||
urls: this.store.findRecord('pki/urls', engine.id).catch((e) => e.httpStatus),
|
||||
crl: this.store.findRecord('pki/crl', engine.id).catch((e) => e.httpStatus),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ export default class PkiConfigurationCreateRoute extends Route {
|
|||
model() {
|
||||
return hash({
|
||||
config: this.store.createRecord('pki/action'),
|
||||
urls: this.getOrCreateUrls(this.secretMountPath.currentPath),
|
||||
urls: this.modelFor('configuration').urls,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,4 @@ export default class PkiConfigurationCreateRoute extends Route {
|
|||
{ label: 'configure' },
|
||||
];
|
||||
}
|
||||
|
||||
async getOrCreateUrls(backend) {
|
||||
try {
|
||||
return this.store.findRecord('pki/urls', backend);
|
||||
} catch (e) {
|
||||
return this.store.createRecord('pki/urls', { id: backend });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,29 @@
|
|||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
|
||||
export default class PkiConfigurationEditRoute extends Route {}
|
||||
@withConfirmLeave('model.config', ['model.urls', 'model.crl'])
|
||||
export default class PkiConfigurationEditRoute extends Route {
|
||||
@service secretMountPath;
|
||||
|
||||
model() {
|
||||
const { urls, crl, engine } = this.modelFor('configuration');
|
||||
return {
|
||||
engineId: engine.id,
|
||||
urls,
|
||||
crl,
|
||||
};
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
controller.breadcrumbs = [
|
||||
{ label: 'secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: this.secretMountPath.currentPath, route: 'overview' },
|
||||
{ label: 'configuration', route: 'configuration.index' },
|
||||
{ label: 'edit' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,46 +11,23 @@ import { hash } from 'rsvp';
|
|||
@withConfig()
|
||||
export default class ConfigurationIndexRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
|
||||
async fetchUrls(backend) {
|
||||
try {
|
||||
return await this.store.findRecord('pki/urls', backend);
|
||||
} catch (e) {
|
||||
return e.httpStatus;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchCrl(backend) {
|
||||
try {
|
||||
return await this.store.findRecord('pki/crl', backend);
|
||||
} catch (e) {
|
||||
return e.httpStatus;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchMountConfig(path) {
|
||||
const mountConfig = await this.store.query('secret-engine', { path });
|
||||
|
||||
async fetchMountConfig(backend) {
|
||||
const mountConfig = await this.store.query('secret-engine', { path: backend });
|
||||
if (mountConfig) {
|
||||
return mountConfig.get('firstObject');
|
||||
}
|
||||
}
|
||||
|
||||
async model() {
|
||||
const backend = this.secretMountPath.currentPath;
|
||||
|
||||
model() {
|
||||
const { urls, crl, engine } = this.modelFor('configuration');
|
||||
return hash({
|
||||
hasConfig: this.shouldPromptConfig,
|
||||
engine: this.modelFor('application'),
|
||||
urls: this.fetchUrls(backend),
|
||||
crl: this.fetchCrl(backend),
|
||||
mountConfig: this.fetchMountConfig(backend),
|
||||
engine,
|
||||
urls,
|
||||
crl,
|
||||
mountConfig: this.fetchMountConfig(engine.id),
|
||||
issuerModel: this.store.createRecord('pki/issuer'),
|
||||
});
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,13 @@
|
|||
configuration.edit
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3">
|
||||
<Icon @name="file-text" @size="24" class="has-text-grey-light" />
|
||||
Edit PKI configuration
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
<Page::PkiConfigurationEdit @urls={{this.model.urls}} @crl={{this.model.crl}} @backend={{this.model.engineId}} />
|
|
@ -299,9 +299,10 @@ module('Acceptance | pki engine route cleanup test', function (hooks) {
|
|||
await click(SELECTORS.configuration.generateRootSave);
|
||||
// Go to list view so we fetch all the issuers
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/issuers`);
|
||||
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
const issuerId = issuers.objectAt(0).id;
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer exists on model');
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer exists on model in list');
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/issuers/${issuerId}/details`);
|
||||
await click(SELECTORS.issuerDetails.configure);
|
||||
issuer = this.store.peekRecord('pki/issuer', issuerId);
|
||||
|
@ -310,7 +311,7 @@ module('Acceptance | pki engine route cleanup test', function (hooks) {
|
|||
assert.true(issuer.hasDirtyAttributes, 'Model is dirty');
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer exists on model');
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer exists on model in overview');
|
||||
issuer = this.store.peekRecord('pki/issuer', issuerId);
|
||||
assert.false(issuer.hasDirtyAttributes, 'Dirty attrs were rolled back');
|
||||
});
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
export const SELECTORS = {
|
||||
// global urls
|
||||
issuingCertificatesLabel: '[data-test-row-label="Issuing certificates"]',
|
||||
issuingCertificatesRowVal: '[data-test-row-value="Issuing certificates"]',
|
||||
crlDistributionPointsLabel: '[data-test-row-label="CRL distribution points"]',
|
||||
crlDistributionPointsRowVal: '[data-test-row-value="CRL distribution points"]',
|
||||
// crl
|
||||
expiryLabel: '[data-test-row-label="Expiry"]',
|
||||
expiryRowVal: '[data-test-row-value="Expiry"]',
|
||||
rebuildLabel: '[data-test-row-label="Auto-rebuild"]',
|
||||
rebuildRowVal: '[data-test-row-value="Auto-rebuild"]',
|
||||
responderApiLabel: '[data-test-row-label="Responder APIs"]',
|
||||
responderApiRowVal: '[data-test-row-value="Responder APIs"]',
|
||||
intervalLabel: '[data-test-row-label="Interval"]',
|
||||
intervalRowVal: '[data-test-row-value="Interval"]',
|
||||
// mount configuration
|
||||
engineTypeLabel: '[data-test-row-label="Secret engine type"]',
|
||||
engineTypeRowVal: '[data-test-row-value="Secret engine type"]',
|
||||
pathLabel: '[data-test-row-label="Path"]',
|
||||
pathRowVal: '[data-test-row-value="Path"]',
|
||||
accessorLabel: '[data-test-row-label="Accessor"]',
|
||||
accessorRowVal: '[data-test-row-value="Accessor"]',
|
||||
localLabel: '[data-test-row-label="Local"]',
|
||||
localRowVal: '[data-test-value-div="Local"]',
|
||||
sealWrapLabel: '[data-test-row-label="Seal wrap"]',
|
||||
sealWrapRowVal: '[data-test-value-div="Seal wrap"]',
|
||||
maxLeaseTtlLabel: '[data-test-row-label="Max lease TTL"]',
|
||||
maxLeaseTtlRowVal: '[data-test-row-value="Max lease TTL"]',
|
||||
allowedManagedKeysLabel: '[data-test-row-label="Allowed managed keys"]',
|
||||
allowedManagedKeysRowVal: '[data-test-value-div="Allowed managed keys"]',
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
export const SELECTORS = {
|
||||
errorBanner: '[data-test-error-banner]',
|
||||
urlsEditSection: '[data-test-urls-edit-section]',
|
||||
urlFieldInput: (attr) => `[data-test-input="${attr}"] textarea`,
|
||||
urlFieldLabel: (attr) => `[data-test-input="${attr}"] label`,
|
||||
crlEditSection: '[data-test-crl-edit-section]',
|
||||
crlToggleInput: (attr) => `[data-test-input="${attr}"] input`,
|
||||
crlTtlInput: (attr) => `[data-test-ttl-value="${attr}"]`,
|
||||
crlFieldLabel: (attr) => `[data-test-input="${attr}"] label`,
|
||||
saveButton: '[data-test-configuration-edit-save]',
|
||||
cancelButton: '[data-test-configuration-edit-cancel]',
|
||||
validationAlert: '[data-test-configuration-edit-validation-alert]',
|
||||
deleteButton: (attr) => `[data-test-input="${attr}"] [data-test-string-list-button="delete"]`,
|
||||
};
|
|
@ -8,7 +8,12 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-configuration-details';
|
||||
|
||||
const SELECTORS = {
|
||||
rowLabel: (attr) => `[data-test-row-label="${attr}"]`,
|
||||
rowValue: (attr) => `[data-test-value-div="${attr}"]`,
|
||||
rowIcon: (attr, icon) => `[data-test-row-value="${attr}"] [data-test-icon="${icon}"]`,
|
||||
};
|
||||
|
||||
module('Integration | Component | Page::PkiConfigurationDetails', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
@ -23,9 +28,13 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
|
|||
this.crl = this.store.createRecord('pki/crl', {
|
||||
id: 'pki-test',
|
||||
expiry: '20h',
|
||||
autoRebuild: false,
|
||||
disable: false,
|
||||
autoRebuild: true,
|
||||
autoRebuildGracePeriod: '13h',
|
||||
enableDelta: true,
|
||||
deltaRebuildInterval: '15m',
|
||||
ocspExpiry: '77h',
|
||||
oscpDisable: true,
|
||||
ocspDisable: false,
|
||||
});
|
||||
this.mountConfig = {
|
||||
id: 'pki-test',
|
||||
|
@ -49,10 +58,10 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
|
|||
);
|
||||
|
||||
assert
|
||||
.dom(SELECTORS.issuingCertificatesLabel)
|
||||
.dom(SELECTORS.rowLabel('Issuing certificates'))
|
||||
.hasText('Issuing certificates', 'issuing certificate row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.issuingCertificatesRowVal)
|
||||
.dom(SELECTORS.rowValue('Issuing certificates'))
|
||||
.hasText('example.com', 'issuing certificate value renders');
|
||||
this.urls.issuingCertificates = null;
|
||||
await render(
|
||||
|
@ -60,13 +69,13 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
|
|||
{ owner: this.engine }
|
||||
);
|
||||
assert
|
||||
.dom(SELECTORS.issuingCertificatesRowVal)
|
||||
.dom(SELECTORS.rowValue('Issuing certificates'))
|
||||
.hasText('None', 'issuing certificate value renders None if none is configured');
|
||||
assert
|
||||
.dom(SELECTORS.crlDistributionPointsLabel)
|
||||
.dom(SELECTORS.rowLabel('CRL distribution points'))
|
||||
.hasText('CRL distribution points', 'crl distribution points row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.crlDistributionPointsRowVal)
|
||||
.dom(SELECTORS.rowValue('CRL distribution points'))
|
||||
.hasText('None', 'crl distribution points value renders None if none is configured');
|
||||
});
|
||||
|
||||
|
@ -76,26 +85,61 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
|
|||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(SELECTORS.expiryLabel).hasText('Expiry', 'crl expiry row label renders');
|
||||
assert.dom(SELECTORS.expiryRowVal).hasText('20h', 'expiry value renders');
|
||||
assert.dom(SELECTORS.rebuildLabel).hasText('Auto-rebuild', 'auto rebuild label renders');
|
||||
assert.dom(SELECTORS.rowLabel('CRL building')).hasText('CRL building', 'crl expiry row label renders');
|
||||
assert.dom(SELECTORS.rowValue('CRL building')).hasText('Enabled', 'enabled renders');
|
||||
assert.dom(SELECTORS.rowValue('Expiry')).hasText('20h', 'expiry value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Auto-rebuild')).hasText('Auto-rebuild', 'auto rebuild label renders');
|
||||
assert.dom(SELECTORS.rowValue('Auto-rebuild')).hasText('On', 'it renders truthy auto build');
|
||||
assert.dom(SELECTORS.rowIcon('Auto-rebuild', 'check-circle'));
|
||||
assert
|
||||
.dom(SELECTORS.rebuildRowVal)
|
||||
.hasText('Off', 'auto-rebuild value renders off if auto rebuild is false');
|
||||
this.crl.autoRebuild = true;
|
||||
.dom(SELECTORS.rowValue('Auto-rebuild grace period'))
|
||||
.hasText('13h', 'it renders auto build grace period');
|
||||
assert.dom(SELECTORS.rowValue('Delta CRL building')).hasText('On', 'it renders truthy delta crl build');
|
||||
assert.dom(SELECTORS.rowIcon('Delta CRL building', 'check-circle'));
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Delta rebuild interval'))
|
||||
.hasText('15m', 'it renders delta build duration');
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Responder APIs'))
|
||||
.hasText('Enabled', 'responder apis value renders Enabled if oscp_disable=false');
|
||||
assert.dom(SELECTORS.rowValue('Interval')).hasText('77h', 'interval value renders');
|
||||
// check falsy aut_rebuild and _enable_delta hides duration values
|
||||
this.crl.autoRebuild = false;
|
||||
this.crl.enableDelta = false;
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} @hasConfig={{true}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
assert.dom(SELECTORS.rowValue('Auto-rebuild')).hasText('Off', 'it renders falsy auto build');
|
||||
assert.dom(SELECTORS.rowIcon('Auto-rebuild', 'x-square'));
|
||||
assert
|
||||
.dom(SELECTORS.rebuildRowVal)
|
||||
.hasText('On', 'auto-rebuild value renders on if auto rebuild is true');
|
||||
assert.dom(SELECTORS.responderApiLabel).hasText('Responder APIs', 'responder apis row label renders');
|
||||
.dom(SELECTORS.rowValue('Auto-rebuild grace period'))
|
||||
.doesNotExist('does not render auto-rebuild grace period');
|
||||
assert.dom(SELECTORS.rowValue('Delta CRL building')).hasText('Off', 'it renders falsy delta cr build');
|
||||
assert.dom(SELECTORS.rowIcon('Delta CRL building', 'x-square'));
|
||||
assert
|
||||
.dom(SELECTORS.responderApiRowVal)
|
||||
.hasText('Enabled', 'responder apis value renders Enabled if oscpDisable is true');
|
||||
assert.dom(SELECTORS.intervalLabel).hasText('Interval', 'interval row label renders');
|
||||
assert.dom(SELECTORS.intervalRowVal).hasText('77h', 'interval value renders');
|
||||
.dom(SELECTORS.rowValue('Delta rebuild interval'))
|
||||
.doesNotExist('does not render delta rebuild duration');
|
||||
|
||||
// check falsy disable and ocsp_disable hides duration values and other params
|
||||
this.crl.autoRebuild = true;
|
||||
this.crl.enableDelta = true;
|
||||
this.crl.disable = true;
|
||||
this.crl.ocspDisable = true;
|
||||
await render(
|
||||
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @mountConfig={{this.mountConfig}} @hasConfig={{true}} />,`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
assert.dom(SELECTORS.rowValue('CRL building')).hasText('Disabled', 'disabled renders');
|
||||
assert.dom(SELECTORS.rowValue('Expiry')).doesNotExist();
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Responder APIs'))
|
||||
.hasText('Disabled', 'responder apis value renders Disabled');
|
||||
assert.dom(SELECTORS.rowValue('Interval')).doesNotExist();
|
||||
assert.dom(SELECTORS.rowValue('Auto-rebuild')).doesNotExist();
|
||||
assert.dom(SELECTORS.rowValue('Auto-rebuild grace period')).doesNotExist();
|
||||
assert.dom(SELECTORS.rowValue('Delta CRL building')).doesNotExist();
|
||||
assert.dom(SELECTORS.rowValue('Delta rebuild interval')).doesNotExist();
|
||||
});
|
||||
|
||||
test('shows the correct information on mount configuration section', async function (assert) {
|
||||
|
@ -104,24 +148,28 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
|
|||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(SELECTORS.engineTypeLabel).hasText('Secret engine type', 'engine type row label renders');
|
||||
assert.dom(SELECTORS.engineTypeRowVal).hasText('pki', 'engine type row value renders');
|
||||
assert.dom(SELECTORS.pathLabel).hasText('Path', 'path row label renders');
|
||||
assert.dom(SELECTORS.pathRowVal).hasText('/pki-test', 'path row value renders');
|
||||
assert.dom(SELECTORS.accessorLabel).hasText('Accessor', 'accessor row label renders');
|
||||
assert.dom(SELECTORS.accessorRowVal).hasText('pki_33345b0d', 'accessor row value renders');
|
||||
assert.dom(SELECTORS.localLabel).hasText('Local', 'local row label renders');
|
||||
assert.dom(SELECTORS.localRowVal).hasText('No', 'local row value renders');
|
||||
assert.dom(SELECTORS.sealWrapLabel).hasText('Seal wrap', 'seal wrap row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.sealWrapRowVal)
|
||||
.dom(SELECTORS.rowLabel('Secret engine type'))
|
||||
.hasText('Secret engine type', 'engine type row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Secret engine type')).hasText('pki', 'engine type row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Path')).hasText('Path', 'path row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Path')).hasText('/pki-test', 'path row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Accessor')).hasText('Accessor', 'accessor row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Accessor')).hasText('pki_33345b0d', 'accessor row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Local')).hasText('Local', 'local row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Local')).hasText('No', 'local row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Seal wrap')).hasText('Seal wrap', 'seal wrap row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Seal wrap'))
|
||||
.hasText('Yes', 'seal wrap row value renders Yes if sealWrap is true');
|
||||
assert.dom(SELECTORS.maxLeaseTtlLabel).hasText('Max lease TTL', 'max lease label renders');
|
||||
assert.dom(SELECTORS.maxLeaseTtlRowVal).hasText('400h', 'max lease value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Max lease TTL')).hasText('Max lease TTL', 'max lease label renders');
|
||||
assert.dom(SELECTORS.rowValue('Max lease TTL')).hasText('400h', 'max lease value renders');
|
||||
assert
|
||||
.dom(SELECTORS.allowedManagedKeysLabel)
|
||||
.dom(SELECTORS.rowLabel('Allowed managed keys'))
|
||||
.hasText('Allowed managed keys', 'allowed managed keys label renders');
|
||||
assert.dom(SELECTORS.allowedManagedKeysRowVal).hasText('Yes', 'allowed managed keys value renders');
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Allowed managed keys'))
|
||||
.hasText('Yes', 'allowed managed keys value renders');
|
||||
});
|
||||
|
||||
test('shows mount configuration when hasConfig is false', async function (assert) {
|
||||
|
@ -133,23 +181,27 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
|
|||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(SELECTORS.engineTypeLabel).hasText('Secret engine type', 'engine type row label renders');
|
||||
assert.dom(SELECTORS.engineTypeRowVal).hasText('pki', 'engine type row value renders');
|
||||
assert.dom(SELECTORS.pathLabel).hasText('Path', 'path row label renders');
|
||||
assert.dom(SELECTORS.pathRowVal).hasText('/pki-test', 'path row value renders');
|
||||
assert.dom(SELECTORS.accessorLabel).hasText('Accessor', 'accessor row label renders');
|
||||
assert.dom(SELECTORS.accessorRowVal).hasText('pki_33345b0d', 'accessor row value renders');
|
||||
assert.dom(SELECTORS.localLabel).hasText('Local', 'local row label renders');
|
||||
assert.dom(SELECTORS.localRowVal).hasText('No', 'local row value renders');
|
||||
assert.dom(SELECTORS.sealWrapLabel).hasText('Seal wrap', 'seal wrap row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.sealWrapRowVal)
|
||||
.dom(SELECTORS.rowLabel('Secret engine type'))
|
||||
.hasText('Secret engine type', 'engine type row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Secret engine type')).hasText('pki', 'engine type row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Path')).hasText('Path', 'path row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Path')).hasText('/pki-test', 'path row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Accessor')).hasText('Accessor', 'accessor row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Accessor')).hasText('pki_33345b0d', 'accessor row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Local')).hasText('Local', 'local row label renders');
|
||||
assert.dom(SELECTORS.rowValue('Local')).hasText('No', 'local row value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Seal wrap')).hasText('Seal wrap', 'seal wrap row label renders');
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Seal wrap'))
|
||||
.hasText('Yes', 'seal wrap row value renders Yes if sealWrap is true');
|
||||
assert.dom(SELECTORS.maxLeaseTtlLabel).hasText('Max lease TTL', 'max lease label renders');
|
||||
assert.dom(SELECTORS.maxLeaseTtlRowVal).hasText('400h', 'max lease value renders');
|
||||
assert.dom(SELECTORS.rowLabel('Max lease TTL')).hasText('Max lease TTL', 'max lease label renders');
|
||||
assert.dom(SELECTORS.rowValue('Max lease TTL')).hasText('400h', 'max lease value renders');
|
||||
assert
|
||||
.dom(SELECTORS.allowedManagedKeysLabel)
|
||||
.dom(SELECTORS.rowLabel('Allowed managed keys'))
|
||||
.hasText('Allowed managed keys', 'allowed managed keys label renders');
|
||||
assert.dom(SELECTORS.allowedManagedKeysRowVal).hasText('Yes', 'allowed managed keys value renders');
|
||||
assert
|
||||
.dom(SELECTORS.rowValue('Allowed managed keys'))
|
||||
.hasText('Yes', 'allowed managed keys value renders');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
/**
|
||||
* 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 { 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 () {
|
||||
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.cancelSpy = sinon.spy();
|
||||
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/crl', {
|
||||
modelName: 'pki/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/urls', {
|
||||
modelName: 'pki/urls',
|
||||
id: this.backend,
|
||||
issuing_certificates: ['hashicorp.com'],
|
||||
crl_distribution_points: ['some-crl-distribution.com'],
|
||||
ocsp_servers: ['ocsp-stuff.com'],
|
||||
});
|
||||
this.urls = this.store.peekRecord('pki/urls', this.backend);
|
||||
this.crl = this.store.peekRecord('pki/crl', this.backend);
|
||||
});
|
||||
|
||||
test('it renders with config data and updates config', async function (assert) {
|
||||
assert.expect(27);
|
||||
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 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 model attributes'
|
||||
);
|
||||
});
|
||||
await render(
|
||||
hbs`
|
||||
<Page::PkiConfigurationEdit
|
||||
@urls={{this.urls}}
|
||||
@crl={{this.crl}}
|
||||
@backend={{this.backend}}
|
||||
/>
|
||||
`,
|
||||
this.context
|
||||
);
|
||||
|
||||
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');
|
||||
|
||||
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/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
|
||||
@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);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'vault/tests/helpers';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
|
||||
module('Unit | Adapter | pki/crl', function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.backend = 'pki-engine';
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on update', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.post(`/${this.backend}/config/crl`, () => {
|
||||
assert.ok(true, 'request made to correct endpoint on update');
|
||||
});
|
||||
|
||||
this.store.pushPayload('pki/crl', {
|
||||
modelName: 'pki/crl',
|
||||
id: this.backend,
|
||||
});
|
||||
|
||||
const model = this.store.peekRecord('pki/crl', this.backend);
|
||||
await model.save();
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on find', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.get(`/${this.backend}/config/crl`, () => {
|
||||
assert.ok(true, 'request is made to correct endpoint on find');
|
||||
return { data: { id: this.backend } };
|
||||
});
|
||||
|
||||
this.store.findRecord('pki/crl', this.backend);
|
||||
});
|
||||
});
|
|
@ -4,50 +4,42 @@
|
|||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { resolve } from 'rsvp';
|
||||
import { setupTest } from 'vault/tests/helpers';
|
||||
|
||||
const storeStub = {
|
||||
pushPayload() {},
|
||||
serializerFor() {
|
||||
return {
|
||||
serializeIntoHash() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
const makeSnapshot = (obj) => {
|
||||
const snapshot = {
|
||||
id: obj.id,
|
||||
record: {
|
||||
...obj,
|
||||
},
|
||||
};
|
||||
snapshot.attr = (attr) => snapshot[attr];
|
||||
return snapshot;
|
||||
};
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
|
||||
module('Unit | Adapter | pki/urls', function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
test('pki url endpoints', function (assert) {
|
||||
let url, method;
|
||||
const adapter = this.owner.factoryFor('adapter:pki/urls').create({
|
||||
ajax: (...args) => {
|
||||
[url, method] = args;
|
||||
return resolve({});
|
||||
},
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.backend = 'pki-engine';
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint on update', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.post(`/${this.backend}/config/urls`, () => {
|
||||
assert.ok(true, 'request made to correct endpoint on update');
|
||||
});
|
||||
|
||||
adapter.createRecord(storeStub, 'pki/urls', makeSnapshot({ id: 'pki-create' }));
|
||||
assert.strictEqual(url, '/v1/pki-create/config/urls', 'create url OK');
|
||||
assert.strictEqual(method, 'POST', 'create method OK');
|
||||
this.store.pushPayload('pki/urls', {
|
||||
modelName: 'pki/urls',
|
||||
id: this.backend,
|
||||
});
|
||||
|
||||
adapter.updateRecord(storeStub, 'pki/urls', makeSnapshot({ id: 'pki-update' }));
|
||||
assert.strictEqual(url, '/v1/pki-update/config/urls', 'update url OK');
|
||||
assert.strictEqual(method, 'PUT', 'update method OK');
|
||||
const model = this.store.peekRecord('pki/urls', this.backend);
|
||||
await model.save();
|
||||
});
|
||||
|
||||
adapter.findRecord(null, 'capabilities', 'pki-find');
|
||||
assert.strictEqual(url, '/v1/pki-find/config/urls', 'find url OK');
|
||||
assert.strictEqual(method, 'GET', 'find method OK');
|
||||
test('it should make request to correct endpoint on find', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.server.get(`/${this.backend}/config/urls`, () => {
|
||||
assert.ok(true, 'request is made to correct endpoint on find');
|
||||
return { data: { id: this.backend } };
|
||||
});
|
||||
|
||||
this.store.findRecord('pki/urls', this.backend);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
export interface FormField {
|
||||
name: string;
|
||||
type: string;
|
||||
options: unknown;
|
||||
options: AttributeOptions;
|
||||
}
|
||||
|
||||
interface AttributeOptions {
|
||||
label: string;
|
||||
mapToBoolean: string;
|
||||
isOppositeValue: boolean;
|
||||
}
|
||||
|
||||
export interface FormFieldGroups {
|
||||
|
@ -40,3 +46,10 @@ export interface Breadcrumb {
|
|||
route?: string;
|
||||
linkExternal?: boolean;
|
||||
}
|
||||
|
||||
export interface TtlEvent {
|
||||
enabled: boolean;
|
||||
seconds: number;
|
||||
timeString: string;
|
||||
goSafeTimeString: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import Model from '@ember-data/model';
|
||||
import { FormField } from 'vault/app-types';
|
||||
|
||||
export default class PkiCrlModel extends Model {
|
||||
autoRebuild: boolean;
|
||||
autoRebuildGracePeriod: string;
|
||||
enableDelta: boolean;
|
||||
expiry: string;
|
||||
deltaRebuildInterval: string;
|
||||
disable: boolean;
|
||||
ocspExpiry: string;
|
||||
ocspDisable: boolean;
|
||||
crlPath: string;
|
||||
formFields: FormField[];
|
||||
get canSet(): boolean;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import Model from '@ember-data/model';
|
||||
|
||||
export default class PkiUrlsModel extends Model {
|
||||
get useOpenAPI(): boolean;
|
||||
getHelpUrl(backendPath: string): string;
|
||||
issuingCertificates: array;
|
||||
crlDistributionPoints: array;
|
||||
ocspServers: array;
|
||||
urlsPath: string;
|
||||
get canSet(): boolean;
|
||||
}
|
Loading…
Reference in New Issue