diff --git a/ui/app/models/pki/pki-role-engine.js b/ui/app/models/pki/pki-role-engine.js
index 8460ca5e75..c2fa644d56 100644
--- a/ui/app/models/pki/pki-role-engine.js
+++ b/ui/app/models/pki/pki-role-engine.js
@@ -22,8 +22,7 @@ export default class PkiRoleEngineModel extends Model {
@attr('string', {
label: 'Issuer reference',
defaultValue: 'default',
- subText:
- 'Specifies the issuer that will be used to create certificates with this role. To find this, run [command]. By default, we will use the mounts default issuer.',
+ subText: `Specifies the issuer that will be used to create certificates with this role. To find this, run read -field=default pki_int/config/issuers in the console. By default, we will use the mounts default issuer.`,
})
issuerRef;
diff --git a/ui/lib/core/addon/components/radio-select-ttl-or-string.hbs b/ui/lib/core/addon/components/radio-select-ttl-or-string.hbs
index 60ff240bc4..cc37e22bd2 100644
--- a/ui/lib/core/addon/components/radio-select-ttl-or-string.hbs
+++ b/ui/lib/core/addon/components/radio-select-ttl-or-string.hbs
@@ -5,6 +5,7 @@
@value="ttl"
@onChange={{this.onRadioButtonChange}}
@groupValue={{this.groupValue}}
+ data-test-radio-button="ttl"
/>
Specific date
@@ -34,14 +36,13 @@
{{#if (eq this.groupValue "specificDate")}}
{{/if}}
diff --git a/ui/lib/core/addon/components/radio-select-ttl-or-string.js b/ui/lib/core/addon/components/radio-select-ttl-or-string.js
index b9d1e29702..e63869bbfa 100644
--- a/ui/lib/core/addon/components/radio-select-ttl-or-string.js
+++ b/ui/lib/core/addon/components/radio-select-ttl-or-string.js
@@ -28,16 +28,21 @@ export default class RadioSelectTtlOrString extends Component {
// Clear the previous selection if they have clicked the other radio button.
if (selection === 'specificDate') {
this.args.model.set('ttl', '');
- this.ttlTime = ''; //clear out the form field
+ this.ttlTime = '';
}
- if (selection === 'tll') {
+ if (selection === 'ttl') {
this.args.model.set('notAfter', '');
- this.notAfter = ''; //clear out the form field
+ this.notAfter = '';
+ this.args.model.set('ttl', this.ttlTime);
}
}
@action setAndBroadcastTtl(value) {
let valueToSet = value.enabled === true ? `${value.seconds}s` : 0;
+ if (this.groupValue === 'specificDate') {
+ // do not save ttl on the model until the ttl radio button is selected
+ return;
+ }
this.args.model.set('ttl', `${valueToSet}`);
}
diff --git a/ui/lib/core/addon/helpers/is-active-route.js b/ui/lib/core/addon/helpers/is-active-route.js
index 36029f8839..9151b5a58a 100644
--- a/ui/lib/core/addon/helpers/is-active-route.js
+++ b/ui/lib/core/addon/helpers/is-active-route.js
@@ -19,7 +19,7 @@ export default Helper.extend({
const currentRoute = router.get('currentRouteName');
let currentURL = router.get('currentURL');
// if we have any query params we want to discard them
- currentURL = currentURL.split('?')[0];
+ currentURL = currentURL?.split('?')[0];
const comparator = isExact ? exact : startsWith;
if (!currentRoute) {
return false;
diff --git a/ui/lib/pki/addon/components/pki-key-parameters.hbs b/ui/lib/pki/addon/components/pki-key-parameters.hbs
index 2715338d00..b27ce153e6 100644
--- a/ui/lib/pki/addon/components/pki-key-parameters.hbs
+++ b/ui/lib/pki/addon/components/pki-key-parameters.hbs
@@ -49,7 +49,7 @@
data-test-input={{attr.name}}
>
{{#each (path-or-array attr.options.possibleValues @model) as |val|}}
-
+
{{or val.displayName val}}
{{/each}}
diff --git a/ui/lib/pki/addon/components/pki-key-usage.hbs b/ui/lib/pki/addon/components/pki-key-usage.hbs
index 2e4e12ffcc..95713271d7 100644
--- a/ui/lib/pki/addon/components/pki-key-usage.hbs
+++ b/ui/lib/pki/addon/components/pki-key-usage.hbs
@@ -8,7 +8,7 @@
data-test-toggle-group={{@group}}
/>
{{#if (get @model prop)}}
-
+
-
+
{{if @model.isNew "Create" "Update"}}
-
+
Cancel
{{#if this.modelValidations.targets.errors}}
diff --git a/ui/tests/helpers/pki-engine.js b/ui/tests/helpers/pki-engine.js
new file mode 100644
index 0000000000..0d89580717
--- /dev/null
+++ b/ui/tests/helpers/pki-engine.js
@@ -0,0 +1,30 @@
+export const PKI_BASE_URL = `/vault/cluster/secrets/backend/pki/roles`;
+
+export const SELECTORS = {
+ // Pki role
+ roleName: '[data-test-input="name"]',
+ issuerRef: '[data-test-input="issuerRef"]',
+ customTtl: '[data-test-field="customTtl"]',
+ backdateValidity: '[data-test-ttl-value="Backdate validity"]',
+ maxTtl: '[data-test-toggle-label="Max TTL"]',
+ generateLease: '[data-test-field="generateLease"]',
+ noStore: '[data-test-field="noStore"]',
+ addBasicConstraints: '[data-test-input="addBasicConstraints"]',
+ domainHandling: '[data-test-toggle-group="Domain handling"]',
+ keyParams: '[data-test-toggle-group="Key parameters"]',
+ keyType: '[data-test-input="keyType"]',
+ keyBits: '[data-test-input="keyBits"]',
+ signatureBits: '[data-test-input="signatureBits"]',
+ keyUsage: '[data-test-toggle-group="Key usage"]',
+ extKeyUsageOids: '[data-test-input="extKeyUsageOids"]',
+ digitalSignature: '[data-test-input="DigitalSignature"]',
+ keyAgreement: '[data-test-input="KeyAgreement"]',
+ keyEncipherment: '[data-test-input="KeyEncipherment"]',
+ any: '[data-test-input="Any"]',
+ serverAuth: '[data-test-input="ServerAuth"]',
+ policyIdentifiers: '[data-test-toggle-group="Policy identifiers"]',
+ san: '[data-test-toggle-group="Subject Alternative Name (SAN) Options"]',
+ additionalSubjectFields: '[data-test-toggle-group="Additional subject fields"]',
+ roleCreateButton: '[data-test-pki-role-save]',
+ roleCancelButton: '[data-test-pki-role-cancel]',
+};
diff --git a/ui/tests/integration/components/pki/pki-key-parameters-test.js b/ui/tests/integration/components/pki/pki-key-parameters-test.js
new file mode 100644
index 0000000000..5e5a9de473
--- /dev/null
+++ b/ui/tests/integration/components/pki/pki-key-parameters-test.js
@@ -0,0 +1,101 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, click, fillIn } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import { setupEngine } from 'ember-engines/test-support';
+import { SELECTORS } from 'vault/tests/helpers/pki-engine';
+
+module('Integration | Component | pki-key-parameters', function (hooks) {
+ setupRenderingTest(hooks);
+ setupEngine(hooks, 'pki');
+
+ hooks.beforeEach(function () {
+ this.store = this.owner.lookup('service:store');
+ this.model = this.store.createRecord('pki/pki-role-engine');
+ this.model.backend = 'pki';
+ });
+
+ test('it should render the component and display the correct defaults', async function (assert) {
+ assert.expect(3);
+ await render(
+ hbs`
+
+ `,
+ { owner: this.engine }
+ );
+ await click(SELECTORS.keyParams);
+ assert.dom(SELECTORS.keyType).hasValue('rsa');
+ assert.dom(SELECTORS.keyBits).hasValue('2048');
+ assert.dom(SELECTORS.signatureBits).hasValue('0');
+ });
+
+ test('it should set the model properties of key_type and key_bits when key_type changes', async function (assert) {
+ assert.expect(11);
+ await render(
+ hbs`
+
+ `,
+ { 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.signatureBits,
+ 0,
+ 'sets the default value for signature_bits on the model.'
+ );
+ await click(SELECTORS.keyParams);
+ 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,
+ 'sets the new selected value for key_bits on the model based on the selection of key_type.'
+ );
+
+ await fillIn(SELECTORS.keyType, 'ed25519');
+ assert.strictEqual(
+ this.model.keyType,
+ 'ed25519',
+ 'sets the new selected value for key_type on the model.'
+ );
+ assert.strictEqual(
+ this.model.keyBits,
+ 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);
+ assert.strictEqual(this.model.keyType, 'ec', 'sets the new selected value for key_type on the model.');
+ assert.strictEqual(
+ this.model.keyBits,
+ 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,
+ 'sets the new selected value for signature_bits on the model.'
+ );
+
+ await fillIn(SELECTORS.signatureBits, '0');
+ assert.strictEqual(
+ this.model.signatureBits,
+ 0,
+ 'sets the default value for signature_bits on the model.'
+ );
+ });
+});
diff --git a/ui/tests/integration/components/pki/pki-key-usage-test.js b/ui/tests/integration/components/pki/pki-key-usage-test.js
new file mode 100644
index 0000000000..b1c7ddc816
--- /dev/null
+++ b/ui/tests/integration/components/pki/pki-key-usage-test.js
@@ -0,0 +1,85 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, click, 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-engine';
+
+module('Integration | Component | pki-key-usage', function (hooks) {
+ setupRenderingTest(hooks);
+ setupEngine(hooks, 'pki');
+
+ hooks.beforeEach(function () {
+ this.store = this.owner.lookup('service:store');
+ this.model = this.store.createRecord('pki/pki-role-engine');
+ this.model.backend = 'pki';
+ });
+
+ test('it should render the component', async function (assert) {
+ assert.expect(7);
+ await render(
+ hbs`
+
+ `,
+ { owner: this.engine }
+ );
+ await click(SELECTORS.keyUsage);
+ assert.strictEqual(findAll('.b-checkbox').length, 19, 'it render 19 checkboxes');
+ assert.dom(SELECTORS.digitalSignature).isChecked('Digital Signature is true by default');
+ assert.dom(SELECTORS.keyAgreement).isChecked('Key Agreement is true by default');
+ assert.dom(SELECTORS.keyEncipherment).isChecked('Key Encipherment is true by default');
+ assert.dom(SELECTORS.any).isNotChecked('Any is false by default');
+ assert.dom(SELECTORS.extKeyUsageOids).exists('Extended Key usage oids renders');
+
+ // check is flexbox by checking the height of the box
+ let groupBoxHeight = document.querySelector('[data-test-surrounding-div="Key usage"]').clientHeight;
+ assert.strictEqual(
+ groupBoxHeight,
+ 518,
+ 'renders the correct height of the box element if the component is rending as a flexbox'
+ );
+ });
+
+ test('it should set the model properties of key_usage and ext_key_usage based on the checkbox selections', async function (assert) {
+ assert.expect(4);
+ await render(
+ hbs`
+
+ `,
+ { owner: this.engine }
+ );
+ // See PKI API docs https://developer.hashicorp.com/vault/api-docs/secret/pki#key_usage
+ assert.deepEqual(
+ this.model.keyUsage,
+ ['DigitalSignature', 'KeyAgreement', 'KeyEncipherment'],
+ 'sets the default values for key_usage on the model.'
+ );
+ assert.strictEqual(
+ this.model.extKeyUsage,
+ undefined,
+ 'sets no default value set for ext_key_usage on load.'
+ );
+
+ await click(SELECTORS.keyUsage);
+ await click(SELECTORS.digitalSignature);
+ await click(SELECTORS.any);
+ await click(SELECTORS.serverAuth);
+
+ assert.deepEqual(
+ this.model.keyUsage,
+ ['KeyAgreement', 'KeyEncipherment'],
+ 'removes digitalSignature from the model when unchecked.'
+ );
+ assert.deepEqual(this.model.extKeyUsage, ['Any', 'ServerAuth'], 'adds new checkboxes to when checked');
+ });
+});
diff --git a/ui/tests/integration/components/pki/pki-role-form-test.js b/ui/tests/integration/components/pki/pki-role-form-test.js
new file mode 100644
index 0000000000..ab521f7ba2
--- /dev/null
+++ b/ui/tests/integration/components/pki/pki-role-form-test.js
@@ -0,0 +1,112 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, click, fillIn } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import { setupEngine } from 'ember-engines/test-support';
+import { SELECTORS } from 'vault/tests/helpers/pki-engine';
+import { setupMirage } from 'ember-cli-mirage/test-support';
+
+module('Integration | Component | pki/role-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/pki-role-engine');
+ this.model.backend = 'pki';
+ });
+
+ test('it should render default fields and toggle groups', async function (assert) {
+ assert.expect(13);
+ await render(
+ hbs`
+
+ `,
+ { owner: this.engine }
+ );
+ assert.dom(SELECTORS.issuerRef).exists('shows form-field issuer ref');
+ assert.dom(SELECTORS.backdateValidity).exists('shows form-field backdate validity');
+ assert.dom(SELECTORS.customTtl).exists('shows custom yielded form field');
+ assert.dom(SELECTORS.maxTtl).exists('shows form-field max ttl');
+ assert.dom(SELECTORS.generateLease).exists('shows form-field generateLease');
+ assert.dom(SELECTORS.noStore).exists('shows form-field no store');
+ assert.dom(SELECTORS.addBasicConstraints).exists('shows form-field add basic constraints');
+ assert.dom(SELECTORS.domainHandling).exists('shows form-field group add domain handling');
+ assert.dom(SELECTORS.keyParams).exists('shows form-field group key params');
+ assert.dom(SELECTORS.keyUsage).exists('shows form-field group key usage');
+ assert.dom(SELECTORS.policyIdentifiers).exists('shows form-field group policy identifiers');
+ assert.dom(SELECTORS.san).exists('shows form-field group SAN');
+ assert.dom(SELECTORS.additionalSubjectFields).exists('shows form-field group additional subject fields');
+ });
+
+ test('it should save a new pki role with various options selected', async function (assert) {
+ // Key usage, Key params and Not valid after options are tested in their respective component tests
+ assert.expect(9);
+ this.server.post(`/${this.model.backend}/roles/test-role`, (schema, req) => {
+ assert.ok(true, 'Request made to save role');
+ const request = JSON.parse(req.requestBody);
+ const roleName = request.name;
+ const allowedDomainsTemplate = request.allowed_domains_template;
+ const policyIdentifiers = request.policy_identifiers;
+ const allowedUriSansTemplate = request.allow_uri_sans_template;
+ const allowedSerialNumbers = request.allowed_serial_numbers;
+
+ assert.strictEqual(roleName, 'test-role', 'correctly sends the role name');
+ assert.true(allowedDomainsTemplate, 'correctly sends allowed_domains_template');
+ assert.strictEqual(policyIdentifiers[0], 'some-oid', 'correctly sends policy_identifiers');
+ assert.true(allowedUriSansTemplate, 'correctly sends allowed_uri_sans_template');
+ assert.strictEqual(
+ allowedSerialNumbers[0],
+ 'some-serial-number',
+ 'correctly sends allowed_serial_numbers'
+ );
+ return {};
+ });
+
+ this.onSave = () => assert.ok(true, 'onSave callback fires on save success');
+
+ await render(
+ hbs`
+
+ `,
+ { owner: this.engine }
+ );
+
+ await click(SELECTORS.roleCreateButton);
+ assert
+ .dom(SELECTORS.roleName)
+ .hasClass('has-error-border', 'shows border error on role name field when no role name is submitted');
+ assert
+ .dom('[data-test-inline-error-message]')
+ .includesText('Name is required.', 'show correct error message');
+
+ await fillIn(SELECTORS.roleName, 'test-role');
+ await click('[data-test-input="addBasicConstraints"]');
+ await click(SELECTORS.domainHandling);
+ await click('[data-test-input="allowedDomainsTemplate"]');
+ await click(SELECTORS.policyIdentifiers);
+ await fillIn('[data-test-input="policyIdentifiers"] [data-test-string-list-input="0"]', 'some-oid');
+ await click(SELECTORS.san);
+ await click('[data-test-input="allowUriSansTemplate"]');
+ await click(SELECTORS.additionalSubjectFields);
+ await fillIn(
+ '[data-test-input="allowedSerialNumbers"] [data-test-string-list-input="0"]',
+ 'some-serial-number'
+ );
+ await click(SELECTORS.roleCreateButton);
+ });
+
+ /* FUTURE TEST TODO:
+ * it should update role
+ * it should unload the record on cancel
+ */
+});
diff --git a/ui/tests/integration/components/pki/radio-select-ttl-or-string-test.js b/ui/tests/integration/components/pki/radio-select-ttl-or-string-test.js
new file mode 100644
index 0000000000..8495fcbfc2
--- /dev/null
+++ b/ui/tests/integration/components/pki/radio-select-ttl-or-string-test.js
@@ -0,0 +1,90 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, click, fillIn } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import { setupEngine } from 'ember-engines/test-support';
+
+module('Integration | Component | radio-select-ttl-or-string', function (hooks) {
+ setupRenderingTest(hooks);
+ setupEngine(hooks, 'pki');
+
+ hooks.beforeEach(function () {
+ this.store = this.owner.lookup('service:store');
+ this.model = this.store.createRecord('pki/pki-role-engine');
+ this.model.backend = 'pki';
+ this.attr = {
+ helpText: '',
+ options: {
+ helperTextEnabled: 'toggled on and shows text',
+ },
+ };
+ });
+
+ test('it should render the component and init with ttl selected', async function (assert) {
+ assert.expect(3);
+ await render(
+ hbs`
+
+
+
+ `,
+ { owner: this.engine }
+ );
+ assert.dom('[data-test-input="ttl"]').exists('shows the TTL component');
+ let inputValue = document.querySelector('[data-test-ttl-value="TTL"]').value;
+ assert.strictEqual(inputValue, '', 'default TTL is empty');
+ assert.dom('[data-test-radio-button="ttl"]').isChecked('ttl is selected by default');
+ });
+
+ test('it should set the model properties ttl or notAfter based on the radio button selections', async function (assert) {
+ assert.expect(8);
+ await render(
+ hbs`
+
+
+
+ `,
+ { owner: this.engine }
+ );
+ assert.dom('[data-test-input="not_after"]').doesNotExist('does not show input field on initial render');
+
+ await click('[data-test-radio-button="not_after"]');
+ assert
+ .dom('[data-test-input="not_after"]')
+ .exists('does show input field after clicking the radio button');
+
+ const utcDate = '1994-11-05T08:15:30-05:0';
+ const ttlDate = 1;
+ await fillIn('[data-test-input="not_after"]', utcDate);
+ assert.strictEqual(
+ this.model.notAfter,
+ utcDate,
+ 'sets the model property notAfter when this value is selected and filled in.'
+ );
+
+ await fillIn('[data-test-ttl-value="TTL"]', ttlDate);
+ assert.strictEqual(this.model.ttl, '', 'No ttl is set because the radio button was not selected.');
+
+ await click('[data-test-radio-button="ttl"]');
+ assert.strictEqual(
+ this.model.notAfter,
+ '',
+ 'The notAfter is cleared on the model because the radio button was selected.'
+ );
+ assert.strictEqual(
+ this.model.ttl,
+ ttlDate,
+ 'The ttl is now saved on the model because the radio button was selected.'
+ );
+
+ await click('[data-test-radio-button="not_after"]');
+ assert.strictEqual(this.model.ttl, '', 'Both ttl and notAfter are cleared.');
+ assert.strictEqual(this.model.notAfter, '', 'Both ttl and notAfter are cleared.');
+ });
+});