mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-06 14:47:01 +02:00
UI: Fix externally mounted engine tooltip bug (#31382)
* adding clause for external plugin to tooltip * fixing mouseover event * fix check * pulling object out of list * fix used parameter * adding comments * fix * fixing tests * adding 1 more test * update tooltip func, remove excess params, update tests
This commit is contained in:
parent
3a37fe4cca
commit
c80c4b4180
@ -48,21 +48,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="has-text-grey is-grid align-items-center linked-block-title">
|
<div class="has-text-grey is-grid align-items-center linked-block-title">
|
||||||
{{#if backend.icon}}
|
{{#if backend.icon}}
|
||||||
<Hds::TooltipButton
|
<Hds::TooltipButton aria-label="Type of backend" @text={{this.generateToolTipText backend}}>
|
||||||
@text={{if
|
|
||||||
backend.isSupportedBackend
|
|
||||||
(concat
|
|
||||||
(get (engines-display-data backend.type) "displayName")
|
|
||||||
(if
|
|
||||||
(eq backend.version 2)
|
|
||||||
" version 2"
|
|
||||||
(if (and (eq backend.version 1) (eq backend.type "kv")) " version 1" "")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
"The UI only supports configuration views for these secret engines. The CLI must be used to manage other engine resources."
|
|
||||||
}}
|
|
||||||
aria-label="Type of backend"
|
|
||||||
>
|
|
||||||
<Icon @name={{backend.icon}} class="has-text-grey-light" />
|
<Icon @name={{backend.icon}} class="has-text-grey-light" />
|
||||||
</Hds::TooltipButton>
|
</Hds::TooltipButton>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -99,6 +85,7 @@
|
|||||||
@hasChevron={{false}}
|
@hasChevron={{false}}
|
||||||
data-test-popup-menu-trigger
|
data-test-popup-menu-trigger
|
||||||
/>
|
/>
|
||||||
|
{{#if (not-eq (get (engines-display-data backend.type) "type") "generic")}}
|
||||||
<dd.Interactive
|
<dd.Interactive
|
||||||
@route={{backend.backendConfigurationLink}}
|
@route={{backend.backendConfigurationLink}}
|
||||||
@model={{backend.id}}
|
@model={{backend.id}}
|
||||||
@ -106,6 +93,7 @@
|
|||||||
>
|
>
|
||||||
View configuration
|
View configuration
|
||||||
</dd.Interactive>
|
</dd.Interactive>
|
||||||
|
{{/if}}
|
||||||
{{#if (not-eq backend.type "cubbyhole")}}
|
{{#if (not-eq backend.type "cubbyhole")}}
|
||||||
<dd.Interactive
|
<dd.Interactive
|
||||||
@color="critical"
|
@color="critical"
|
||||||
|
@ -13,6 +13,7 @@ import type FlashMessageService from 'vault/services/flash-messages';
|
|||||||
import type SecretsEngineResource from 'vault/resources/secrets/engine';
|
import type SecretsEngineResource from 'vault/resources/secrets/engine';
|
||||||
import type ApiService from 'vault/services/api';
|
import type ApiService from 'vault/services/api';
|
||||||
import type RouterService from '@ember/routing/router-service';
|
import type RouterService from '@ember/routing/router-service';
|
||||||
|
import engineDisplayData from 'vault/helpers/engines-display-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module SecretEngineList handles the display of the list of secret engines, including the filtering.
|
* @module SecretEngineList handles the display of the list of secret engines, including the filtering.
|
||||||
@ -67,6 +68,28 @@ export default class SecretEngineList extends Component<Args> {
|
|||||||
return sortedBackends;
|
return sortedBackends;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateToolTipText = (backend: SecretsEngineResource) => {
|
||||||
|
const displayData = engineDisplayData(backend.type);
|
||||||
|
|
||||||
|
if (!displayData) {
|
||||||
|
return;
|
||||||
|
} else if (backend.isSupportedBackend) {
|
||||||
|
if (backend.type === 'kv') {
|
||||||
|
// If the backend is a KV engine, include the version in the tooltip.
|
||||||
|
return `${displayData.displayName} version ${backend.version}`;
|
||||||
|
} else {
|
||||||
|
return `${displayData.displayName}`;
|
||||||
|
}
|
||||||
|
} else if (displayData.type === 'generic') {
|
||||||
|
// If a mounted engine type doesn't match any known type, the type is returned as 'generic' and set this tooltip.
|
||||||
|
// Handles issue when a user externally mounts an engine that doesn't follow the expected naming conventions for what's in the binary, despite being a valid engine.
|
||||||
|
return 'This plugin is not supported by the UI. Please use the CLI to manage this engine.';
|
||||||
|
} else {
|
||||||
|
// If the engine type is recognized but not supported, we only show configuration view and set this tooltip.
|
||||||
|
return 'The UI only supports configuration views for these secret engines. The CLI must be used to manage other engine resources.';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Filtering & searching
|
// Filtering & searching
|
||||||
get secretEngineArrayByType() {
|
get secretEngineArrayByType() {
|
||||||
const arrayOfAllEngineTypes = this.sortedDisplayableBackends.map((modelObject) => modelObject.engineType);
|
const arrayOfAllEngineTypes = this.sortedDisplayableBackends.map((modelObject) => modelObject.engineType);
|
||||||
|
@ -23,5 +23,15 @@ import { ALL_ENGINES } from 'vault/utils/all-engines-metadata';
|
|||||||
*/
|
*/
|
||||||
export default function engineDisplayData(methodType: string) {
|
export default function engineDisplayData(methodType: string) {
|
||||||
const engine = ALL_ENGINES?.find((t) => t.type === methodType);
|
const engine = ALL_ENGINES?.find((t) => t.type === methodType);
|
||||||
|
if (!engine && methodType) {
|
||||||
|
// Fallback to a generic engine if no match found but type is provided
|
||||||
|
return {
|
||||||
|
displayName: 'Generic plugin',
|
||||||
|
type: 'generic',
|
||||||
|
glyph: 'lock',
|
||||||
|
mountCategory: ['secret', 'auth'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: BUSL-1.1
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { click, fillIn, currentRouteName, visit, currentURL, triggerEvent } from '@ember/test-helpers';
|
import { click, fillIn, currentRouteName, visit, currentURL } from '@ember/test-helpers';
|
||||||
import { selectChoose } from 'ember-power-select/test-support';
|
import { selectChoose } from 'ember-power-select/test-support';
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
@ -86,57 +86,6 @@ module('Acceptance | secret-engine list view', function (hooks) {
|
|||||||
await runCmd(deleteEngineCmd('aws'));
|
await runCmd(deleteEngineCmd('aws'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('hovering over the icon of an unsupported engine shows unsupported tooltip', async function (assert) {
|
|
||||||
await visit('/vault/secrets');
|
|
||||||
await page.enableEngine();
|
|
||||||
await click(MOUNT_BACKEND_FORM.mountType('nomad'));
|
|
||||||
await click(GENERAL.submitButton);
|
|
||||||
|
|
||||||
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-type'), 'nomad');
|
|
||||||
|
|
||||||
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
|
||||||
assert
|
|
||||||
.dom('.hds-tooltip-container')
|
|
||||||
.hasText(
|
|
||||||
'The UI only supports configuration views for these secret engines. The CLI must be used to manage other engine resources.',
|
|
||||||
'shows tooltip text for unsupported engine'
|
|
||||||
);
|
|
||||||
// cleanup
|
|
||||||
await runCmd(deleteEngineCmd('nomad'));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('hovering over the icon of a supported engine shows engine name', async function (assert) {
|
|
||||||
await visit('/vault/secrets');
|
|
||||||
await page.enableEngine();
|
|
||||||
await click(MOUNT_BACKEND_FORM.mountType('ssh'));
|
|
||||||
await click(GENERAL.submitButton);
|
|
||||||
await click(GENERAL.breadcrumbLink('Secrets'));
|
|
||||||
|
|
||||||
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-type'), 'ssh');
|
|
||||||
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
|
||||||
assert.dom('.hds-tooltip-container').hasText('SSH', 'shows tooltip for SSH without version');
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
await runCmd(deleteEngineCmd('ssh'));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('hovering over the icon of a kv engine shows engine name and version', async function (assert) {
|
|
||||||
await visit('/vault/secrets');
|
|
||||||
|
|
||||||
await page.enableEngine();
|
|
||||||
await click(MOUNT_BACKEND_FORM.mountType('kv'));
|
|
||||||
await fillIn(GENERAL.inputByAttr('path'), `kv-${this.uid}`);
|
|
||||||
await click(GENERAL.submitButton);
|
|
||||||
await click(GENERAL.breadcrumbLink('Secrets'));
|
|
||||||
|
|
||||||
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-name'), `kv-${this.uid}`);
|
|
||||||
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
|
||||||
assert.dom('.hds-tooltip-container').hasText('KV version 2', 'shows tooltip for kv version 2');
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
await runCmd(deleteEngineCmd('kv'));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('enterprise: cannot view list without permissions inside a namespace', async function (assert) {
|
test('enterprise: cannot view list without permissions inside a namespace', async function (assert) {
|
||||||
this.namespace = `ns-${this.uid}`;
|
this.namespace = `ns-${this.uid}`;
|
||||||
const enginePath1 = `kv-t1-${this.uid}`;
|
const enginePath1 = `kv-t1-${this.uid}`;
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
|
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'vault/tests/helpers';
|
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||||
import { render, click, find, findAll } from '@ember/test-helpers';
|
import { render, click, find, findAll, triggerEvent } from '@ember/test-helpers';
|
||||||
import { hbs } from 'ember-cli-htmlbars';
|
import { hbs } from 'ember-cli-htmlbars';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||||
import { overrideResponse } from 'vault/tests/helpers/stubs';
|
import { overrideResponse } from 'vault/tests/helpers/stubs';
|
||||||
import { clickTrigger } from 'ember-power-select/test-support/helpers';
|
import { clickTrigger } from 'ember-power-select/test-support/helpers';
|
||||||
|
import { selectChoose } from 'ember-power-select/test-support';
|
||||||
import { createSecretsEngine } from 'vault/tests/helpers/secret-engine/secret-engine-helpers';
|
import { createSecretsEngine } from 'vault/tests/helpers/secret-engine/secret-engine-helpers';
|
||||||
import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
|
import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
|
||||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||||
@ -33,18 +33,19 @@ module('Integration | Component | secret-engine/list', function (hooks) {
|
|||||||
this.flashSuccessSpy = sinon.spy(this.flashMessages, 'success');
|
this.flashSuccessSpy = sinon.spy(this.flashMessages, 'success');
|
||||||
this.flashDangerSpy = sinon.spy(this.flashMessages, 'danger');
|
this.flashDangerSpy = sinon.spy(this.flashMessages, 'danger');
|
||||||
this.uid = uuidv4();
|
this.uid = uuidv4();
|
||||||
// generate a model of cubbyhole, kv2, and nomad
|
// generate a model of cubbyhole, kv, and nomad
|
||||||
this.secretEngineModels = [
|
this.secretEngineModels = [
|
||||||
createSecretsEngine(undefined, 'cubbyhole', 'cubbyhole-test'),
|
createSecretsEngine(undefined, 'cubbyhole', 'cubbyhole-test'),
|
||||||
createSecretsEngine(undefined, 'kv', 'kv2-test'),
|
createSecretsEngine(undefined, 'kv', 'kv-test'),
|
||||||
createSecretsEngine(undefined, 'aws', 'aws-1'),
|
createSecretsEngine(undefined, 'aws', 'aws-1'),
|
||||||
createSecretsEngine(undefined, 'aws', 'aws-2'),
|
createSecretsEngine(undefined, 'aws', 'aws-2'),
|
||||||
createSecretsEngine(undefined, 'nomad', 'nomad-test'),
|
createSecretsEngine(undefined, 'nomad', 'nomad-test'),
|
||||||
|
createSecretsEngine(undefined, 'badType', 'external-test'),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it allows you to disable an engine', async function (assert) {
|
test('it allows you to disable an engine', async function (assert) {
|
||||||
const enginePath = 'kv2-test';
|
const enginePath = 'kv-test';
|
||||||
this.server.delete(`sys/mounts/${enginePath}`, () => {
|
this.server.delete(`sys/mounts/${enginePath}`, () => {
|
||||||
assert.true(true, 'Request is made to delete engine');
|
assert.true(true, 'Request is made to delete engine');
|
||||||
return overrideResponse(204);
|
return overrideResponse(204);
|
||||||
@ -63,6 +64,54 @@ module('Integration | Component | secret-engine/list', function (hooks) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('hovering over the icon of an external unrecognized engine type sets unrecognized tooltip text', async function (assert) {
|
||||||
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
||||||
|
|
||||||
|
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-name'), 'external-test');
|
||||||
|
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom('.hds-tooltip-container')
|
||||||
|
.hasText(
|
||||||
|
'This plugin is not supported by the UI. Please use the CLI to manage this engine.',
|
||||||
|
'shows tooltip text for unsupported engine'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hovering over the icon of an unsupported engine sets unsupported tooltip text', async function (assert) {
|
||||||
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
||||||
|
|
||||||
|
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-type'), 'nomad');
|
||||||
|
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom('.hds-tooltip-container')
|
||||||
|
.hasText(
|
||||||
|
'The UI only supports configuration views for these secret engines. The CLI must be used to manage other engine resources.',
|
||||||
|
'shows tooltip text for unsupported engine'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hovering over the icon of a supported engine sets engine name as tooltip', async function (assert) {
|
||||||
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
||||||
|
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-name'), 'aws-1');
|
||||||
|
|
||||||
|
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
||||||
|
|
||||||
|
assert.dom('.hds-tooltip-container').hasText('AWS', 'shows tooltip text for supported engine with name');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hovering over the icon of a kv engine shows engine name and version', async function (assert) {
|
||||||
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}}/>`);
|
||||||
|
|
||||||
|
await selectChoose(GENERAL.searchSelect.trigger('filter-by-engine-name'), `kv-test`);
|
||||||
|
|
||||||
|
await triggerEvent('.hds-tooltip-button', 'mouseenter');
|
||||||
|
assert
|
||||||
|
.dom('.hds-tooltip-container')
|
||||||
|
.hasText('KV version 1', 'shows tooltip text for kv engine with version');
|
||||||
|
});
|
||||||
|
|
||||||
test('it adds disabled css styling to unsupported secret engines', async function (assert) {
|
test('it adds disabled css styling to unsupported secret engines', async function (assert) {
|
||||||
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
||||||
assert
|
assert
|
||||||
|
Loading…
Reference in New Issue
Block a user