ui: generate pki key (#18268)

* create generate key form

* disable key bits unless key type selected

* add create method to adapter, update serializer to remove type

* refactor key parameters component

* convert to typescript

* refactor routes to add controller breadcrumbs

* remove unnecessary attr

* revert typescript changes

* add validations to key type

* fix tests

* cleanup breadcrumbs

* update tests, change all bit types to strings

* add form test
This commit is contained in:
claire bontempo
2022-12-08 14:22:33 -08:00
committed by GitHub
parent a8d316de85
commit 176e39e877
23 changed files with 504 additions and 84 deletions

View File

@@ -2,6 +2,15 @@ import ApplicationAdapter from '../application';
import { encodePath } from 'vault/utils/path-encoding-helpers';
export default class PkiKeyAdapter extends ApplicationAdapter {
namespace = 'v1';
createRecord(store, type, snapshot) {
const { record } = snapshot;
const url = this.getUrl(record.backend) + '/generate/' + record.type;
return this.ajax(url, 'POST', { data: this.serialize(snapshot) }).then((resp) => {
return resp;
});
}
getUrl(backend, id) {
const url = `${this.buildURL()}/${encodePath(backend)}`;
if (id) {

View File

@@ -1,17 +1,41 @@
import Model, { attr } from '@ember-data/model';
import { inject as service } from '@ember/service';
import { withFormFields } from 'vault/decorators/model-form-fields';
import { withModelValidations } from 'vault/decorators/model-validations';
@withFormFields(['keyId', 'keyName', 'keyType', 'keyBits'])
const validations = {
type: [{ type: 'presence', message: 'Type is required.' }],
keyType: [{ type: 'presence', message: 'Please select a key type.' }],
};
const displayFields = ['keyId', 'keyName', 'keyType', 'keyBits'];
const formFieldGroups = [{ default: ['keyName', 'type'] }, { 'Key parameters': ['keyType', 'keyBits'] }];
@withModelValidations(validations)
@withFormFields(displayFields, formFieldGroups)
export default class PkiKeyModel extends Model {
@service secretMountPath;
@attr('boolean') isDefault;
@attr('string', { possibleValues: ['internal', 'external'] }) type;
@attr('string', { detailsLabel: 'Key ID' }) keyId;
@attr('string') keyName;
@attr('string') keyType;
@attr('string') keyBits;
@attr('string', { subText: 'Optional, human-readable name for this key.' }) keyName;
@attr('string') privateKey;
@attr('string', {
noDefault: true,
possibleValues: ['internal', 'exported'],
subText:
'The type of operation. If exported, the private key will be returned in the response; if internal the private key will not be returned and cannot be retrieved later.',
})
type;
@attr('string', {
noDefault: true,
possibleValues: ['rsa', 'ec', 'ed25519'],
subText: 'The type of key that will be generated. Must be rsa, ed25519, or ec. ',
})
keyType;
@attr('string', {
label: 'Key bits',
noDefault: true,
subText: 'Bit length of the key to generate.',
})
keyBits; // no possibleValues because dependent on selected key type
get backend() {
return this.secretMountPath.currentPath;

View File

@@ -175,15 +175,15 @@ export default class PkiRoleModel extends Model {
@attr('string', {
label: 'Key bits',
defaultValue: 2048,
defaultValue: '2048',
})
keyBits; // keyBits is a conditional value based on keyType. The model param is handled in the pkiKeyParameters component.
keyBits; // no possibleValues because options are dependent on selected key type
@attr('number', {
label: 'Signature bits',
subText: `Only applicable for key_type 'RSA'. Ignore for other key types.`,
defaultValue: 0,
possibleValues: [0, 256, 384, 512],
defaultValue: '0',
possibleValues: ['0', '256', '384', '512'],
})
signatureBits;
/* End of overriding Key parameters options */

View File

@@ -2,6 +2,10 @@ import ApplicationSerializer from '../application';
export default class PkiKeySerializer extends ApplicationSerializer {
primaryKey = 'key_id';
attrs = {
type: { serialize: false },
};
// rehydrate each keys model so all model attributes are accessible from the LIST response
normalizeItems(payload) {
if (payload.data) {

View File

@@ -109,11 +109,16 @@ label {
.input.variable {
font-family: $family-monospace;
}
.select select[disabled],
.input[disabled],
.textarea[disabled] {
border-color: $grey-light;
background-color: $white-ter;
color: $grey-light;
background-color: $ui-gray-100;
color: $ui-gray-500;
&:hover {
border-color: $grey-light;
}
}
.control {
@@ -335,7 +340,6 @@ select.has-error-border {
border: 1px solid $red-500;
}
.autocomplete-input {
background: $white !important;
border: 1px solid $grey-light;

View File

@@ -33,7 +33,13 @@
{{else}}
<div class="control is-expanded">
<div class="select is-fullwidth">
<select name={{@attr.name}} id={{@attr.name}} onchange={{this.onChangeWithEvent}} data-test-input={{@attr.name}}>
<select
class="{{if this.validationError 'has-error-border'}}"
name={{@attr.name}}
id={{@attr.name}}
onchange={{this.onChangeWithEvent}}
data-test-input={{@attr.name}}
>
{{#if @attr.options.noDefault}}
<option value="">
Select one
@@ -46,6 +52,9 @@
{{/each}}
</select>
</div>
{{#if this.validationError}}
<AlertInline @type="danger" @message={{this.validationError}} @paddingTop={{true}} />
{{/if}}
</div>
{{/if}}
{{else if (and (eq @attr.type "string") (eq @attr.options.editType "boolean"))}}

View File

@@ -1,15 +1,3 @@
<PageHeader as |p|>
<p.top>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-key-details-title>
<Icon @name="certificate" @size="24" class="has-text-grey-light" />
View key
</h1>
</p.levelLeft>
</PageHeader>
<Toolbar>
<ToolbarActions>
<ConfirmAction
@@ -21,6 +9,7 @@
>
Delete
</ConfirmAction>
<div class="toolbar-separator"></div>
<ToolbarLink @route="keys.key.edit" @model={{@key.model.name}}>
Edit key
</ToolbarLink>
@@ -28,10 +17,24 @@
</Toolbar>
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#if @key.privateKey}}
<div class="has-top-margin-m">
<AlertBanner
@title="Next steps"
@type="warning"
@message="This private key material will only be available once. Copy or download it now."
/>
</div>
{{/if}}
{{#each @key.formFields as |attr|}}
<InfoTableRow
@label={{capitalize (or attr.options.detailsLabel attr.options.label (humanize (dasherize attr.name)))}}
@value={{get @key attr.name}}
/>
{{/each}}
{{#if @key.privateKey}}
<InfoTableRow @label="Private key">
<MaskedInput @value={{@key.privateKey}} @name="Private key" @displayOnly={{true}} @allowCopy={{true}} />
</InfoTableRow>
{{/if}}
</div>

View File

@@ -18,15 +18,6 @@ export default class PkiKeyDetails extends Component<Args> {
@service declare readonly router: RouterService;
@service declare readonly flashMessages: FlashMessageService;
get breadcrumbs() {
return [
{ label: 'secrets', route: 'secrets', linkExternal: true },
{ label: this.args.key.backend || 'pki', route: 'overview' },
{ label: 'keys', route: 'keys.index' },
{ label: this.args.key.keyId },
];
}
@action
async deleteKey() {
try {

View File

@@ -0,0 +1,47 @@
<form {{on "submit" (perform this.save)}}>
<div class="box is-sideless is-fullwidth is-marginless">
<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
<NamespaceReminder @mode={{if @model.isNew "create" "update"}} @noun="PKI key" />
{{#each @model.formFieldGroups as |fieldGroup|}}
{{#each-in fieldGroup as |group fields|}}
{{#if (eq group "Key parameters")}}
<PkiKeyParameters @model={{@model}} @fields={{fields}} @modelValidations={{this.modelValidations}} />
{{else}}
{{#each fields as |attr|}}
<FormField
data-test-field={{attr}}
@attr={{attr}}
@model={{@model}}
@modelValidations={{this.modelValidations}}
@showHelpText={{false}}
/>
{{/each}}
{{/if}}
{{/each-in}}
{{/each}}
</div>
<div class="has-top-padding-s">
<button
type="submit"
class="button is-primary {{if this.save.isRunning 'is-loading'}}"
disabled={{this.save.isRunning}}
data-test-pki-key-save
>
{{if @model.isNew "Create" "Update"}}
</button>
<button
type="button"
class="button has-left-margin-s"
disabled={{this.save.isRunning}}
{{on "click" this.cancel}}
data-test-pki-key-cancel
>
Cancel
</button>
{{#if this.invalidFormAlert}}
<div class="control">
<AlertInline @type="danger" @paddingTop={{true}} @message={{this.invalidFormAlert}} @mimicRefresh={{true}} />
</div>
{{/if}}
</div>
</form>

View File

@@ -0,0 +1,55 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { task } from 'ember-concurrency';
import { tracked } from '@glimmer/tracking';
import errorMessage from 'vault/utils/error-message';
/**
* @module PkiKeyForm
* PkiKeyForm components are used to create and update PKI keys.
*
* @example
* ```js
* <PkiKeyForm @model={{this.model}}/>
* ```
*
* @param {Object} model - pki/key model.
* @callback onCancel - Callback triggered when cancel button is clicked.
* @callback onSave - Callback triggered on save success.
*/
export default class PkiKeyForm extends Component {
@service store;
@service flashMessages;
@tracked errorBanner;
@tracked invalidFormAlert;
@tracked modelValidations;
@task
*save(event) {
event.preventDefault();
try {
const { isValid, state, invalidFormMessage } = this.args.model.validate();
this.modelValidations = isValid ? null : state;
this.invalidFormAlert = invalidFormMessage;
if (isValid) {
const { isNew, keyName } = this.args.model;
yield this.args.model.save();
this.flashMessages.success(`Successfully ${isNew ? 'generated' : 'updated'} the key ${keyName}.`);
this.args.onSave();
}
} catch (error) {
this.errorBanner = errorMessage(error);
this.invalidFormAlert = 'There was an error submitting this form.';
}
}
@action
cancel() {
const method = this.args.model.isNew ? 'unloadRecord' : 'rollbackAttributes';
this.args.model[method]();
this.args.onCancel();
}
}

View File

@@ -1,17 +1,39 @@
{{#each @fields as |attr|}}
{{#if (eq attr.name "keyBits")}}
<div class="field">
<FormFieldLabel for={{attr.name}} @label={{attr.options.label}} />
<FormFieldLabel for={{attr.name}} @label={{attr.options.label}} @subText={{attr.options.subText}} />
<div class="control is-expanded">
<div class="select is-fullwidth">
<select name={{attr.name}} id={{attr.name}} onchange={{this.onKeyBitsChange}} data-test-input={{attr.name}}>
{{#each this.keyBitOptions as |val|}}
<option selected={{eq (get @model this.valuePath) (or val.value val)}} value={{val}}>
{{val}}
</option>
{{/each}}
</select>
</div>
<ToolTip @verticalPosition="above" @horizontalPosition="center" as |T|>
<T.Trigger tabindex="-1">
<div class="select is-fullwidth">
<select
id={{attr.name}}
name={{attr.name}}
data-test-input={{attr.name}}
disabled={{unless @model.keyType true}}
{{on "change" this.onKeyBitsChange}}
>
{{#if (and attr.options.noDefault (not @model.keyType))}}
<option value="">
Select one
</option>
{{/if}}
{{#each this.keyBitOptions as |val|}}
<option selected={{eq (get @model "keyBits") val}} value={{val}}>
{{val}}
</option>
{{/each}}
</select>
</div>
</T.Trigger>
{{#unless @model.keyType}}
<T.Content @defaultClass="tool-tip smaller-font">
<div class="box">
Choose a key type before specifying bit length.
</div>
</T.Content>
{{/unless}}
</ToolTip>
</div>
</div>
{{else}}
@@ -21,7 +43,7 @@
@model={{@model}}
@modelValidations={{@modelValidations}}
@showHelpText={{false}}
@onChange={{this.onSignatureBitsOrKeyTypeChange}}
@onChange={{this.handleSelection}}
/>
{{/if}}
{{/each}}

View File

@@ -3,43 +3,40 @@ import { action } from '@ember/object';
/**
* @module PkiKeyParameters
* PkiKeyParameters components are used to display a list of key bit options depending on the selected key type.
* PkiKeyParameters components are used to display a list of key bit options depending on the selected key type. The key bits field is disabled until a key type is selected.
* If the component renders in a group, other attrs may be passed in and will be rendered using the <FormField> component
* @example
* ```js
* <PkiKeyParameters @model={{@model}} @fields={{fields}}/>
* ```
* @param {class} model - The pki/role model.
* @param {string} fields - The name of the fields created in the model. In this case, it's the "Key parameters" fields.
* @param {string} fields - Array of attributes from a formFieldGroup generated by the @withFormFields decorator ex: [{ name: 'attrName', type: 'string', options: {...} }]
*/
// first value in array is the default bits for that key type
const KEY_BITS_OPTIONS = {
rsa: [2048, 3072, 4096],
ec: [256, 224, 384, 521],
ed25519: [0],
any: [0],
rsa: ['2048', '3072', '4096'],
ec: ['256', '224', '384', '521'],
ed25519: ['0'],
any: ['0'],
};
export default class PkiKeyParameters extends Component {
// TODO clarify types here
get keyBitOptions() {
return KEY_BITS_OPTIONS[this.args.model.keyType];
}
get keyBitsDefault() {
return Number(KEY_BITS_OPTIONS[this.args.model.keyType][0]);
}
@action handleSelection(name, selection) {
this.args.model[name] = selection;
@action onKeyBitsChange(selection) {
this.args.model.set('keyBits', Number(selection.target.value));
}
@action onSignatureBitsOrKeyTypeChange(name, selection) {
if (name === 'signatureBits') {
this.args.model.set(name, Number(selection));
}
if (name === 'keyType') {
this.args.model.set('keyBits', this.keyBitsDefault);
this.args.model.keyBits = Object.keys(KEY_BITS_OPTIONS).includes(selection)
? KEY_BITS_OPTIONS[selection][0]
: '';
}
}
@action onKeyBitsChange({ target }) {
this.handleSelection(target.name, target.value);
}
}

View File

@@ -1,3 +1,18 @@
import Route from '@ember/routing/route';
import PkiKeysIndexRoute from '.';
import { inject as service } from '@ember/service';
import { withConfirmLeave } from 'core/decorators/confirm-leave';
export default class PkiKeysCreateRoute extends Route {}
@withConfirmLeave()
export default class PkiKeysCreateRoute extends PkiKeysIndexRoute {
@service store;
@service secretMountPath;
model() {
return this.store.createRecord('pki/key');
}
setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);
controller.breadcrumbs.push({ label: 'generate' });
}
}

View File

@@ -25,4 +25,14 @@ export default class PkiKeysIndexRoute extends Route {
}
});
}
setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);
const backend = this.secretMountPath.currentPath || 'pki';
controller.breadcrumbs = [
{ label: 'secrets', route: 'secrets', linkExternal: true },
{ label: backend, route: 'overview' },
{ label: 'keys', route: 'keys.index' },
];
}
}

View File

@@ -1,3 +1,8 @@
import Route from '@ember/routing/route';
import PkiKeysIndexRoute from '.';
export default class PkiKeyDetailsRoute extends Route {}
export default class PkiKeyDetailsRoute extends PkiKeysIndexRoute {
setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);
controller.breadcrumbs.push({ label: resolvedModel.id });
}
}

View File

@@ -1,3 +1,8 @@
import Route from '@ember/routing/route';
import PkiKeysIndexRoute from '.';
export default class PkiKeyEditRoute extends Route {}
export default class PkiKeyEditRoute extends PkiKeysIndexRoute {
setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);
controller.breadcrumbs.push({ label: resolvedModel.id, route: 'keys.key.details' }, { label: 'edit' });
}
}

View File

@@ -1 +1,16 @@
keys.generate
<PageHeader as |p|>
<p.top>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-pki-key-page-title>
Generate key
</h1>
</p.levelLeft>
</PageHeader>
<PkiKeyForm
@model={{this.model}}
@onCancel={{transition-to "vault.cluster.secrets.backend.pki.keys.index"}}
@onSave={{transition-to "vault.cluster.secrets.backend.pki.keys.key.details" this.model.id}}
/>

View File

@@ -1 +1,13 @@
<PageHeader as |p|>
<p.top>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-key-details-title>
<Icon @name="certificate" @size="24" class="has-text-grey-light" />
View key
</h1>
</p.levelLeft>
</PageHeader>
<Page::PkiKeyDetails @key={{this.model}} />

View File

@@ -1 +1,11 @@
keys.key.:id.edit
<PageHeader as |p|>
<p.top>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-key-details-title>
<Icon @name="certificate" @size="24" class="has-text-grey-light" />
Edit key
</h1>
</p.levelLeft>
</PageHeader>

View File

@@ -0,0 +1,9 @@
export const SELECTORS = {
keyCreateButton: '[data-test-pki-key-save]',
keyCancelButton: '[data-test-pki-key-cancel]',
keyNameInput: '[data-test-input="keyName"]',
typeInput: '[data-test-input="type"]',
keyTypeInput: '[data-test-input="keyType"]',
keyBitsInput: '[data-test-input="keyBits"]',
inlineAlert: '[data-test-inline-error-message]',
};

View File

@@ -0,0 +1,175 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click, fillIn, findAll } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupEngine } from 'ember-engines/test-support';
import { SELECTORS } from 'vault/tests/helpers/pki/keys/form';
import { setupMirage } from 'ember-cli-mirage/test-support';
module('Integration | Component | pki key form', function (hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
setupEngine(hooks, 'pki'); // https://github.com/ember-engines/ember-engines/pull/653
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.model = this.store.createRecord('pki/key');
this.backend = 'pki-test';
this.secretMountPath = this.owner.lookup('service:secret-mount-path');
this.secretMountPath.currentPath = this.backend;
});
test('it should render fields and show validation messages', async function (assert) {
assert.expect(7);
await render(
hbs`
<PkiKeyForm
@model={{this.model}}
@onCancel={{this.onCancel}}
@onSave={{this.onSave}}
/>
`,
{ owner: this.engine }
);
assert.dom(SELECTORS.keyNameInput).exists('renders name input');
assert.dom(SELECTORS.typeInput).exists('renders type input');
assert.dom(SELECTORS.keyTypeInput).exists('renders key type input');
assert.dom(SELECTORS.keyBitsInput).exists('renders key bits input');
await click(SELECTORS.keyCreateButton);
const [type, keyType, formErrorCount] = findAll('[data-test-inline-error-message]');
assert.strictEqual(type.innerText, 'Type is required.', 'renders presence validation for type of key');
assert.strictEqual(
keyType.innerText,
'Please select a key type.',
'renders selection prompt for key type'
);
assert.strictEqual(
formErrorCount.innerText,
'There are 2 errors with this form.',
'renders correct form error count'
);
});
test('it generates a key type=exported', async function (assert) {
assert.expect(4);
this.server.post(`/${this.backend}/keys/generate/exported`, (schema, req) => {
assert.ok(true, 'Request made to the correct endpoint to generate exported key');
const request = JSON.parse(req.requestBody);
assert.propEqual(
request,
{
key_name: 'test-key',
key_type: 'rsa',
key_bits: '2048',
},
'sends params in correct type'
);
return {};
});
this.onSave = () => assert.ok(true, 'onSave callback fires on save success');
await render(
hbs`
<PkiKeyForm
@model={{this.model}}
@onCancel={{this.onCancel}}
@onSave={{this.onSave}}
/>
`,
{ owner: this.engine }
);
await fillIn(SELECTORS.keyNameInput, 'test-key');
await fillIn(SELECTORS.typeInput, 'exported');
assert.dom(SELECTORS.keyBitsInput).isDisabled('key bits disabled when no key type selected');
await fillIn(SELECTORS.keyTypeInput, 'rsa');
await click(SELECTORS.keyCreateButton);
});
test('it generates a key type=internal', async function (assert) {
assert.expect(4);
this.server.post(`/${this.backend}/keys/generate/internal`, (schema, req) => {
assert.ok(true, 'Request made to the correct endpoint to generate internal key');
const request = JSON.parse(req.requestBody);
assert.propEqual(
request,
{
key_name: 'test-key',
key_type: 'rsa',
key_bits: '2048',
},
'sends params in correct type'
);
return {};
});
this.onSave = () => assert.ok(true, 'onSave callback fires on save success');
await render(
hbs`
<PkiKeyForm
@model={{this.model}}
@onCancel={{this.onCancel}}
@onSave={{this.onSave}}
/>
`,
{ owner: this.engine }
);
await fillIn(SELECTORS.keyNameInput, 'test-key');
await fillIn(SELECTORS.typeInput, 'internal');
assert.dom(SELECTORS.keyBitsInput).isDisabled('key bits disabled when no key type selected');
await fillIn(SELECTORS.keyTypeInput, 'rsa');
await click(SELECTORS.keyCreateButton);
});
test('it should rollback attributes or unload record on cancel', async function (assert) {
assert.expect(2);
this.onCancel = () => assert.ok(true, 'onCancel callback fires');
await render(
hbs`
<PkiKeyForm
@model={{this.model}}
@onCancel={{this.onCancel}}
@onSave={{this.onSave}}
/>
`,
{ owner: this.engine }
);
await click(SELECTORS.keyCancelButton);
assert.true(this.model.isDestroyed, 'new model is unloaded on cancel');
/* COMMENT IN WHEN EDIT IS COMPLETE
this.store.pushPayload('pki/key', {
modelName: this.modelName,
keyName: 'test-key',
type: 'exported',
keyId: 'some-key-id',
keyType: 'rsa',
keyBits: '2048',
};);
this.model = this.store.peekRecord('pki/key', this.data.keyId;
await render(
hbs`
<PkiKeyForm
@model={{this.model}}
@onCancel={{this.onCancel}}
@onSave={{this.onSave}}
/>
`,
{ owner: this.engine }
);
await fillIn(SELECTORS.keyNameInput, 'new-name');
await click(SELECTORS.keyCancelButton);
assert.strictEqual(this.model.keyName, undefined, 'Model attributes rolled back on cancel');
*/
});
/* FUTURE TEST TODO:
* it should update key
*/
});

View File

@@ -27,7 +27,7 @@ module('Integration | Component | pki key details page', function (hooks) {
});
test('it renders the page component and deletes a key', async function (assert) {
assert.expect(7);
assert.expect(6);
this.server.delete(`${this.backend}/key/${this.model.keyId}`, () => {
assert.ok(true, 'confirming delete fires off destroyRecord()');
});
@@ -39,7 +39,6 @@ module('Integration | Component | pki key details page', function (hooks) {
{ owner: this.engine }
);
assert.dom(SELECTORS.title).containsText('View key', 'title renders');
assert.dom(SELECTORS.keyIdValue).hasText(' 724862ff-6438-bad0-b598-77a6c7f4e934', 'key id renders');
assert.dom(SELECTORS.keyNameValue).hasText('test-key', 'key name renders');
assert.dom(SELECTORS.keyTypeValue).hasText('ec', 'key type renders');

View File

@@ -5,7 +5,7 @@ import { hbs } from 'ember-cli-htmlbars';
import { setupEngine } from 'ember-engines/test-support';
import { SELECTORS } from 'vault/tests/helpers/pki/roles/form';
module('Integration | Component | pki-key-parameters', function (hooks) {
module('Integration | Component | pki key parameters', function (hooks) {
setupRenderingTest(hooks);
setupEngine(hooks, 'pki');
@@ -47,17 +47,17 @@ module('Integration | Component | pki-key-parameters', function (hooks) {
{ owner: this.engine }
);
assert.strictEqual(this.model.keyType, 'rsa', 'sets the default value for key_type on the model.');
assert.strictEqual(this.model.keyBits, 2048, 'sets the default value for key_bits on the model.');
assert.strictEqual(this.model.keyBits, '2048', 'sets the default value for key_bits on the model.');
assert.strictEqual(
this.model.signatureBits,
0,
'0',
'sets the default value for signature_bits on the model.'
);
await fillIn(SELECTORS.keyType, 'ec');
assert.strictEqual(this.model.keyType, 'ec', 'sets the new selected value for key_type on the model.');
assert.strictEqual(
this.model.keyBits,
256,
'256',
'sets the new selected value for key_bits on the model based on the selection of key_type.'
);
@@ -69,30 +69,30 @@ module('Integration | Component | pki-key-parameters', function (hooks) {
);
assert.strictEqual(
this.model.keyBits,
0,
'0',
'sets the new selected value for key_bits on the model based on the selection of key_type.'
);
await fillIn(SELECTORS.keyType, 'ec');
await fillIn(SELECTORS.keyBits, 384);
await fillIn(SELECTORS.keyBits, '384');
assert.strictEqual(this.model.keyType, 'ec', 'sets the new selected value for key_type on the model.');
assert.strictEqual(
this.model.keyBits,
384,
'384',
'sets the new selected value for key_bits on the model based on the selection of key_type.'
);
await fillIn(SELECTORS.signatureBits, '384');
assert.strictEqual(
this.model.signatureBits,
384,
'384',
'sets the new selected value for signature_bits on the model.'
);
await fillIn(SELECTORS.signatureBits, '0');
assert.strictEqual(
this.model.signatureBits,
0,
'0',
'sets the default value for signature_bits on the model.'
);
});