mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 02:02:43 +00:00
UI: Fix aws credential generation sending ttl value when off (#27366)
* do not send ttl if unset for aws credentials * test coverage * remove comment * add changelog * Update aws test, cancel button is secondary
This commit is contained in:
3
changelog/27366.txt
Normal file
3
changelog/27366.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
ui: Fix a bug where disabling TTL on the AWS credential form would still send TTL value
|
||||||
|
```
|
||||||
@@ -16,7 +16,7 @@ export default ApplicationAdapter.extend({
|
|||||||
if (roleType === 'iam_user') {
|
if (roleType === 'iam_user') {
|
||||||
method = 'GET';
|
method = 'GET';
|
||||||
} else {
|
} else {
|
||||||
if (ttl !== undefined) {
|
if (ttl) {
|
||||||
data.ttl = ttl;
|
data.ttl = ttl;
|
||||||
}
|
}
|
||||||
if (roleType === 'assumed_role' && roleArn) {
|
if (roleType === 'assumed_role' && roleArn) {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export default Model.extend({
|
|||||||
editType: 'ttl',
|
editType: 'ttl',
|
||||||
defaultValue: '3600s',
|
defaultValue: '3600s',
|
||||||
setDefault: true,
|
setDefault: true,
|
||||||
|
ttlOffValue: '',
|
||||||
label: 'TTL',
|
label: 'TTL',
|
||||||
helpText:
|
helpText:
|
||||||
'Specifies the TTL for the use of the STS token. Valid only when credential_type is assumed_role, federation_token, or session_token.',
|
'Specifies the TTL for the use of the STS token. Valid only when credential_type is assumed_role, federation_token, or session_token.',
|
||||||
@@ -66,7 +67,7 @@ export default Model.extend({
|
|||||||
iam_user: ['credentialType'],
|
iam_user: ['credentialType'],
|
||||||
assumed_role: ['credentialType', 'ttl', 'roleArn'],
|
assumed_role: ['credentialType', 'ttl', 'roleArn'],
|
||||||
federation_token: ['credentialType', 'ttl'],
|
federation_token: ['credentialType', 'ttl'],
|
||||||
session_token: ['ttl'],
|
session_token: ['credentialType', 'ttl'],
|
||||||
};
|
};
|
||||||
if (this.accessKey || this.securityToken) {
|
if (this.accessKey || this.securityToken) {
|
||||||
return expandAttributeMeta(this, DISPLAY_FIELDS.slice(0));
|
return expandAttributeMeta(this, DISPLAY_FIELDS.slice(0));
|
||||||
|
|||||||
@@ -147,6 +147,7 @@
|
|||||||
<Hds::Button
|
<Hds::Button
|
||||||
@text="Cancel"
|
@text="Cancel"
|
||||||
@route="vault.cluster.secrets.backend.list-root"
|
@route="vault.cluster.secrets.backend.list-root"
|
||||||
|
@color="secondary"
|
||||||
@model={{this.backendPath}}
|
@model={{this.backendPath}}
|
||||||
data-test-secret-generate-cancel={{true}}
|
data-test-secret-generate-cancel={{true}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -158,7 +158,13 @@ export default class FormFieldComponent extends Component {
|
|||||||
@action
|
@action
|
||||||
setAndBroadcastTtl(value) {
|
setAndBroadcastTtl(value) {
|
||||||
const alwaysSendValue = this.valuePath === 'expiry' || this.valuePath === 'safetyBuffer';
|
const alwaysSendValue = this.valuePath === 'expiry' || this.valuePath === 'safetyBuffer';
|
||||||
const valueToSet = value.enabled === true || alwaysSendValue ? `${value.seconds}s` : 0;
|
const attrOptions = this.args.attr.options || {};
|
||||||
|
let valueToSet = 0;
|
||||||
|
if (value.enabled || alwaysSendValue) {
|
||||||
|
valueToSet = `${value.seconds}s`;
|
||||||
|
} else if (Object.keys(attrOptions).includes('ttlOffValue')) {
|
||||||
|
valueToSet = attrOptions.ttlOffValue;
|
||||||
|
}
|
||||||
this.setAndBroadcast(`${valueToSet}`);
|
this.setAndBroadcast(`${valueToSet}`);
|
||||||
}
|
}
|
||||||
@action
|
@action
|
||||||
|
|||||||
@@ -3,16 +3,18 @@
|
|||||||
* SPDX-License-Identifier: BUSL-1.1
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { click, fillIn, findAll, currentURL, find, settled, waitUntil } from '@ember/test-helpers';
|
import { click, fillIn, currentURL, find, settled, waitUntil } from '@ember/test-helpers';
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import authPage from 'vault/tests/pages/auth';
|
import authPage from 'vault/tests/pages/auth';
|
||||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||||
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||||
|
|
||||||
module('Acceptance | aws secret backend', function (hooks) {
|
module('Acceptance | aws secret backend', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
this.uid = uuidv4();
|
this.uid = uuidv4();
|
||||||
@@ -20,9 +22,13 @@ module('Acceptance | aws secret backend', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('aws backend', async function (assert) {
|
test('aws backend', async function (assert) {
|
||||||
assert.expect(12);
|
|
||||||
const path = `aws-${this.uid}`;
|
const path = `aws-${this.uid}`;
|
||||||
const roleName = 'awsrole';
|
const roleName = 'awsrole';
|
||||||
|
this.server.post(`/${path}/creds/${roleName}`, (_, req) => {
|
||||||
|
const payload = JSON.parse(req.requestBody);
|
||||||
|
assert.deepEqual(payload, { role_arn: 'foobar' }, 'does not send TTL when unchecked');
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
await enablePage.enable('aws', path);
|
await enablePage.enable('aws', path);
|
||||||
await settled();
|
await settled();
|
||||||
@@ -31,28 +37,25 @@ module('Acceptance | aws secret backend', function (hooks) {
|
|||||||
await click('[data-test-secret-backend-configure]');
|
await click('[data-test-secret-backend-configure]');
|
||||||
|
|
||||||
assert.strictEqual(currentURL(), `/vault/settings/secrets/configure/${path}`);
|
assert.strictEqual(currentURL(), `/vault/settings/secrets/configure/${path}`);
|
||||||
assert.ok(findAll('[data-test-aws-root-creds-form]').length, 'renders the empty root creds form');
|
assert.dom('[data-test-aws-root-creds-form]').exists();
|
||||||
assert.ok(findAll('[data-test-aws-link="root-creds"]').length, 'renders the root creds link');
|
assert.dom('[data-test-aws-link="root-creds"]').exists();
|
||||||
assert.ok(findAll('[data-test-aws-link="leases"]').length, 'renders the leases config link');
|
assert.dom('[data-test-aws-link="leases"]').exists();
|
||||||
|
|
||||||
await fillIn('[data-test-aws-input="accessKey"]', 'foo');
|
await fillIn('[data-test-aws-input="accessKey"]', 'foo');
|
||||||
await fillIn('[data-test-aws-input="secretKey"]', 'bar');
|
await fillIn('[data-test-aws-input="secretKey"]', 'bar');
|
||||||
|
|
||||||
await click('[data-test-aws-input="root-save"]');
|
await click('[data-test-aws-input="root-save"]');
|
||||||
|
|
||||||
assert.ok(
|
assert
|
||||||
find('[data-test-flash-message]').textContent.trim(),
|
.dom('[data-test-flash-message]:last-of-type [data-test-flash-message-body]')
|
||||||
`The backend configuration saved successfully!`
|
.includesText(`The backend configuration saved successfully!`);
|
||||||
);
|
|
||||||
|
|
||||||
await click('[data-test-aws-link="leases"]');
|
await click('[data-test-aws-link="leases"]');
|
||||||
|
|
||||||
await click('[data-test-aws-input="lease-save"]');
|
await click('[data-test-aws-input="lease-save"]');
|
||||||
|
assert
|
||||||
assert.ok(
|
.dom('[data-test-flash-message]:last-of-type [data-test-flash-message-body]')
|
||||||
find('[data-test-flash-message]').textContent.trim(),
|
.includesText(`The backend configuration saved successfully!`);
|
||||||
`The backend configuration saved successfully!`
|
|
||||||
);
|
|
||||||
|
|
||||||
await click('[data-test-backend-view-link]');
|
await click('[data-test-backend-view-link]');
|
||||||
|
|
||||||
@@ -60,10 +63,7 @@ module('Acceptance | aws secret backend', function (hooks) {
|
|||||||
|
|
||||||
await click('[data-test-secret-create]');
|
await click('[data-test-secret-create]');
|
||||||
|
|
||||||
assert.ok(
|
assert.dom('[data-test-secret-header]').includesText('AWS Role');
|
||||||
find('[data-test-secret-header]').textContent.includes('AWS Role'),
|
|
||||||
`aws: renders the create page`
|
|
||||||
);
|
|
||||||
|
|
||||||
await fillIn('[data-test-input="name"]', roleName);
|
await fillIn('[data-test-input="name"]', roleName);
|
||||||
|
|
||||||
@@ -78,7 +78,19 @@ module('Acceptance | aws secret backend', function (hooks) {
|
|||||||
await click(`[data-test-secret-breadcrumb="${path}"] a`);
|
await click(`[data-test-secret-breadcrumb="${path}"] a`);
|
||||||
|
|
||||||
assert.strictEqual(currentURL(), `/vault/secrets/${path}/list`);
|
assert.strictEqual(currentURL(), `/vault/secrets/${path}/list`);
|
||||||
assert.ok(findAll(`[data-test-secret-link="${roleName}"]`).length, `aws: role shows in the list`);
|
assert.dom(`[data-test-secret-link="${roleName}"]`).exists();
|
||||||
|
|
||||||
|
// check that generates credentials flow is correct
|
||||||
|
await click(`[data-test-secret-link="${roleName}"]`);
|
||||||
|
assert.dom('h1').hasText('Generate AWS Credentials');
|
||||||
|
assert.dom('[data-test-input="credentialType"]').hasValue('iam_user');
|
||||||
|
await fillIn('[data-test-input="credentialType"]', 'assumed_role');
|
||||||
|
await click('[data-test-ttl-toggle="TTL"]');
|
||||||
|
assert.dom('[data-test-ttl-toggle="TTL"]').isNotChecked();
|
||||||
|
await fillIn('[data-test-input="roleArn"]', 'foobar');
|
||||||
|
await click('[data-test-secret-generate]');
|
||||||
|
assert.dom('[data-test-warning]').exists('Shows access warning after generation');
|
||||||
|
await click('[data-test-secret-generate-back]');
|
||||||
|
|
||||||
//and delete
|
//and delete
|
||||||
await click(`[data-test-secret-link="${roleName}"] [data-test-popup-menu-trigger]`);
|
await click(`[data-test-secret-link="${roleName}"] [data-test-popup-menu-trigger]`);
|
||||||
|
|||||||
@@ -127,14 +127,41 @@ module('Integration | Component | form field', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it renders: editType ttl', async function (assert) {
|
test('it renders: editType ttl', async function (assert) {
|
||||||
const [model, spy] = await setup.call(this, createAttr('foo', null, { editType: 'ttl' }));
|
const [model, spy] = await setup.call(
|
||||||
|
this,
|
||||||
|
createAttr('foo', null, {
|
||||||
|
editType: 'ttl',
|
||||||
|
helperTextDisabled: 'TTL is disabled',
|
||||||
|
helperTextEnabled: 'TTL is enabled',
|
||||||
|
})
|
||||||
|
);
|
||||||
assert.ok(component.hasTTLPicker, 'renders the ttl-picker component');
|
assert.ok(component.hasTTLPicker, 'renders the ttl-picker component');
|
||||||
|
assert.dom('[data-test-ttl-form-subtext]').hasText('TTL is disabled');
|
||||||
|
assert.dom('[data-test-ttl-toggle]').isNotChecked();
|
||||||
await component.fields.objectAt(0).toggleTtl();
|
await component.fields.objectAt(0).toggleTtl();
|
||||||
await component.fields.objectAt(0).select('h').change();
|
await component.fields.objectAt(0).select('h').change();
|
||||||
await component.fields.objectAt(0).ttlTime('3');
|
await component.fields.objectAt(0).ttlTime('3');
|
||||||
const expectedSeconds = `${3 * 3600}s`;
|
const expectedSeconds = `${3 * 3600}s`;
|
||||||
assert.strictEqual(model.get('foo'), expectedSeconds);
|
assert.strictEqual(model.get('foo'), expectedSeconds);
|
||||||
assert.ok(spy.calledWith('foo', expectedSeconds), 'onChange called with correct args');
|
assert.ok(spy.calledWith('foo', expectedSeconds), 'onChange called with correct args');
|
||||||
|
await component.fields.objectAt(0).toggleTtl();
|
||||||
|
assert.ok(spy.calledWith('foo', '0'), 'onChange called with 0 when toggle off');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders: editType ttl with special settings', async function (assert) {
|
||||||
|
const [model, spy] = await setup.call(
|
||||||
|
this,
|
||||||
|
createAttr('foo', null, {
|
||||||
|
editType: 'ttl',
|
||||||
|
setDefault: '3600s',
|
||||||
|
ttlOffValue: '',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.ok(component.hasTTLPicker, 'renders the ttl-picker component');
|
||||||
|
assert.dom('[data-test-ttl-toggle]').isChecked();
|
||||||
|
await component.fields.objectAt(0).toggleTtl();
|
||||||
|
assert.strictEqual(model.get('foo'), '');
|
||||||
|
assert.ok(spy.calledWith('foo', ''), 'onChange called with correct args');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders: editType ttl without toggle', async function (assert) {
|
test('it renders: editType ttl without toggle', async function (assert) {
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ module('Unit | Adapter | aws credential', function (hooks) {
|
|||||||
[storeStub, type, makeSnapshot({ credentialType: 'assumed_role' })],
|
[storeStub, type, makeSnapshot({ credentialType: 'assumed_role' })],
|
||||||
'POST',
|
'POST',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'assumed_role type no arn, ttl empty',
|
||||||
|
[storeStub, type, makeSnapshot({ credentialType: 'assumed_role', ttl: '' })],
|
||||||
|
'POST',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'assumed_role type no arn',
|
'assumed_role type no arn',
|
||||||
[storeStub, type, makeSnapshot({ credentialType: 'assumed_role', ttl: '3h' })],
|
[storeStub, type, makeSnapshot({ credentialType: 'assumed_role', ttl: '3h' })],
|
||||||
|
|||||||
Reference in New Issue
Block a user