UI: Fix LDAP Mirage Handler (#28432)

* update ldap mirage scenario to allow check-in/check-out action

* update libraries test to mount engine

* update mirage, fix tests

* update lease renew CLI command

* fix test

* update tests
This commit is contained in:
Chelsea Shaw
2024-09-20 13:44:29 -05:00
committed by GitHub
parent e848f1683d
commit 2ce68778e4
9 changed files with 195 additions and 56 deletions

View File

@@ -27,7 +27,7 @@ export default class LdapLibraryDetailsAccountsPageComponent extends Component<A
@tracked checkOutTtl: string | null = null;
get cliCommand() {
return `vault lease renew ad/library/${this.args.library.name}/check-out/:lease_id`;
return `vault lease renew ${this.args.library.backend}/library/${this.args.library.name}/check-out/:lease_id`;
}
@action
setTtl(data: TtlEvent) {

View File

@@ -35,17 +35,6 @@ export default function (server) {
};
};
// mount
server.post('/sys/mounts/:path', () => new Response(204));
server.get('/sys/internal/ui/mounts/:path', () => ({
data: {
accessor: 'ldap_ade94329',
type: 'ldap',
path: 'ldap-test/',
uuid: '35e9119d-5708-4b6b-58d2-f913e27f242d',
config: {},
},
}));
// config
server.post('/:backend/config', (schema, req) => createOrUpdateRecord(schema, req, 'ldapConfigs'));
server.get('/:backend/config', (schema, req) => getRecord(schema, req, 'ldapConfigs'));
@@ -67,8 +56,60 @@ export default function (server) {
server.post('/:backend/library/:name', (schema, req) => createOrUpdateRecord(schema, req, 'ldapLibraries'));
server.get('/:backend/library/:name', (schema, req) => getRecord(schema, req, 'ldapLibraries'));
server.get('/:backend/library', (schema) => listRecords(schema, 'ldapLibraries'));
server.get('/:backend/library/:name/status', () => ({
'bob.johnson': { available: false, borrower_client_token: '8b80c305eb3a7dbd161ef98f10ea60a116ce0910' },
'mary.smith': { available: true },
}));
server.get('/:backend/library/:name/status', (schema) => {
const data = schema.db['ldapAccountStatuses'].reduce((prev, curr) => {
prev[curr.account] = {
available: curr.available,
borrower_client_token: curr.borrower_client_token,
};
return prev;
}, {});
return { data };
});
// check-out / check-in
server.post('/:backend/library/:set_name/check-in', (schema, req) => {
// Check-in makes an unavailable account available again
const { service_account_names } = JSON.parse(req.requestBody);
const dbCollection = schema.db['ldapAccountStatuses'];
const updated = dbCollection.find(service_account_names).map((f) => ({
...f,
available: true,
borrower_client_token: undefined,
}));
updated.forEach((u) => {
dbCollection.update(u.id, u);
});
return {
data: {
check_ins: service_account_names,
},
};
});
server.post('/:backend/library/:set_name/check-out', (schema, req) => {
const { set_name, backend } = req.params;
const dbCollection = schema.db['ldapAccountStatuses'];
const available = dbCollection.where({ available: true });
if (available) {
return Response(404, {}, { errors: ['no accounts available to check out'] });
}
const checkOut = {
...available[0],
available: false,
borrower_client_token: crypto.randomUUID(),
};
dbCollection.update(checkOut.id, checkOut);
return {
request_id: '364a17d4-e5ab-998b-ceee-b49929229e0c',
lease_id: `${backend}/library/${set_name}/check-out/aoBsaBEI4PK96VnukubvYDlZ`,
renewable: true,
lease_duration: 36000,
data: {
password: crypto.randomUUID(),
service_account_name: checkOut.account,
},
wrap_info: null,
warnings: null,
auth: null,
};
});
}

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { Model } from 'miragejs';
export default Model.extend({
account: '', // should match ID
library: '',
available: false,
borrower_client_token: undefined,
});

View File

@@ -4,8 +4,19 @@
*/
export default function (server) {
server.create('ldap-config', { path: 'kubernetes' });
server.create('ldap-config', { path: 'kubernetes', backend: 'ldap-test' });
server.create('ldap-role', 'static', { name: 'static-role' });
server.create('ldap-role', 'dynamic', { name: 'dynamic-role' });
server.create('ldap-library', { name: 'test-library' });
server.create('ldap-account-status', {
id: 'bob.johnson',
account: 'bob.johnson',
available: false,
borrower_client_token: '8b80c305eb3a7dbd161ef98f10ea60a116ce0910',
});
server.create('ldap-account-status', {
id: 'mary.smith',
account: 'mary.smith',
available: true,
});
}

View File

@@ -6,11 +6,13 @@
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { v4 as uuidv4 } from 'uuid';
import ldapMirageScenario from 'vault/mirage/scenarios/ldap';
import ldapHandlers from 'vault/mirage/handlers/ldap';
import authPage from 'vault/tests/pages/auth';
import { click } from '@ember/test-helpers';
import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers';
import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands';
module('Acceptance | ldap | libraries', function (hooks) {
setupApplicationTest(hooks);
@@ -19,21 +21,41 @@ module('Acceptance | ldap | libraries', function (hooks) {
hooks.beforeEach(async function () {
ldapHandlers(this.server);
ldapMirageScenario(this.server);
this.backend = `ldap-test-${uuidv4()}`;
await authPage.login();
return visitURL('libraries');
// mount & configure
await runCmd([
mountEngineCmd('ldap', this.backend),
`write ${this.backend}/config binddn=foo bindpass=bar url=http://localhost:8208`,
]);
return visitURL('libraries', this.backend);
});
hooks.afterEach(async function () {
await runCmd(deleteEngineCmd(this.backend));
});
test('it should show libraries on overview page', async function (assert) {
await visitURL('overview', this.backend);
assert.dom('[data-test-libraries-count]').hasText('1');
});
test('it should transition to create library route on toolbar link click', async function (assert) {
await click('[data-test-toolbar-action="library"]');
assert.true(isURL('libraries/create'), 'Transitions to library create route on toolbar link click');
assert.true(
isURL('libraries/create', this.backend),
'Transitions to library create route on toolbar link click'
);
});
test('it should transition to library details route on list item click', async function (assert) {
await click('[data-test-list-item-link] a');
assert.true(
isURL('libraries/test-library/details/accounts'),
isURL('libraries/test-library/details/accounts', this.backend),
'Transitions to library details accounts route on list item click'
);
assert.dom('[data-test-account-name]').exists({ count: 2 }, 'lists the accounts');
assert.dom('[data-test-checked-out-account]').exists({ count: 1 }, 'lists the checked out accounts');
});
test('it should transition to routes from list item action menu', async function (assert) {
@@ -44,7 +66,7 @@ module('Acceptance | ldap | libraries', function (hooks) {
await click(`[data-test-${action}]`);
const uri = action === 'details' ? 'details/accounts' : action;
assert.true(
isURL(`libraries/test-library/${uri}`),
isURL(`libraries/test-library/${uri}`, this.backend),
`Transitions to ${action} route on list item action menu click`
);
await click('[data-test-breadcrumb="libraries"] a');
@@ -55,13 +77,13 @@ module('Acceptance | ldap | libraries', function (hooks) {
await click('[data-test-list-item-link] a');
await click('[data-test-tab="config"]');
assert.true(
isURL('libraries/test-library/details/configuration'),
isURL('libraries/test-library/details/configuration', this.backend),
'Transitions to configuration route on tab click'
);
await click('[data-test-tab="accounts"]');
assert.true(
isURL('libraries/test-library/details/accounts'),
isURL('libraries/test-library/details/accounts', this.backend),
'Transitions to accounts route on tab click'
);
});
@@ -69,6 +91,9 @@ module('Acceptance | ldap | libraries', function (hooks) {
test('it should transition to routes from library details toolbar links', async function (assert) {
await click('[data-test-list-item-link] a');
await click('[data-test-edit]');
assert.true(isURL('libraries/test-library/edit'), 'Transitions to credentials route from toolbar link');
assert.true(
isURL('libraries/test-library/edit', this.backend),
'Transitions to credentials route from toolbar link'
);
});
});

View File

@@ -6,12 +6,14 @@
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { v4 as uuidv4 } from 'uuid';
import ldapMirageScenario from 'vault/mirage/scenarios/ldap';
import ldapHandlers from 'vault/mirage/handlers/ldap';
import authPage from 'vault/tests/pages/auth';
import { click, fillIn, visit } from '@ember/test-helpers';
import { selectChoose } from 'ember-power-select/test-support';
import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers';
import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands';
module('Acceptance | ldap | overview', function (hooks) {
setupApplicationTest(hooks);
@@ -19,77 +21,101 @@ module('Acceptance | ldap | overview', function (hooks) {
hooks.beforeEach(async function () {
ldapHandlers(this.server);
this.backend = `ldap-test-${uuidv4()}`;
this.mountAndConfig = (backend) => {
return runCmd([
mountEngineCmd('ldap', backend),
`write ${backend}/config binddn=foo bindpass=bar url=http://localhost:8208`,
]);
};
return authPage.login();
});
test('it should transition to ldap overview on mount success', async function (assert) {
const backend = 'ldap-test-mount';
await visit('/vault/secrets');
await click('[data-test-enable-engine]');
await click('[data-test-mount-type="ldap"]');
await fillIn('[data-test-input="path"]', 'ldap-test');
await fillIn('[data-test-input="path"]', backend);
await click('[data-test-mount-submit]');
assert.true(isURL('overview'), 'Transitions to ldap overview route on mount success');
assert.true(isURL('overview', backend), 'Transitions to ldap overview route on mount success');
assert.dom('[data-test-header-title]').hasText(backend);
// cleanup mounted engine
await visit('/vault/secrets');
await runCmd(deleteEngineCmd(backend));
});
test('it should transition to routes on tab link click', async function (assert) {
assert.expect(4);
await this.mountAndConfig(this.backend);
await visitURL('overview');
await visitURL('overview', this.backend);
for (const tab of ['roles', 'libraries', 'config', 'overview']) {
await click(`[data-test-tab="${tab}"]`);
const route = tab === 'config' ? 'configuration' : tab;
assert.true(isURL(route), `Transitions to ${route} route on tab link click`);
assert.true(isURL(route, this.backend), `Transitions to ${route} route on tab link click`);
}
});
test('it should transition to configuration route when engine is not configured', async function (assert) {
await visitURL('overview');
await runCmd(mountEngineCmd('ldap', this.backend));
await visitURL('overview', this.backend);
await click('[data-test-config-cta] a');
assert.true(isURL('configure'), 'Transitions to configure route on cta link click');
assert.true(isURL('configure', this.backend), 'Transitions to configure route on cta link click');
await click('[data-test-breadcrumb="ldap-test"] a');
await click(`[data-test-breadcrumb="${this.backend}"] a`);
await click('[data-test-toolbar-action="config"]');
assert.true(isURL('configure'), 'Transitions to configure route on toolbar link click');
assert.true(isURL('configure', this.backend), 'Transitions to configure route on toolbar link click');
});
// including a test for the configuration route here since it is the only one needed
test('it should transition to configuration edit on toolbar link click', async function (assert) {
ldapMirageScenario(this.server);
await visitURL('overview');
await this.mountAndConfig(this.backend);
await visitURL('overview', this.backend);
await click('[data-test-tab="config"]');
await click('[data-test-toolbar-config-action]');
assert.true(isURL('configure'), 'Transitions to configure route on toolbar link click');
assert.true(isURL('configure', this.backend), 'Transitions to configure route on toolbar link click');
});
test('it should transition to create role route on card action link click', async function (assert) {
ldapMirageScenario(this.server);
await visitURL('overview');
await this.mountAndConfig(this.backend);
await visitURL('overview', this.backend);
await click('[data-test-overview-card="Roles"] a');
assert.true(isURL('roles/create'), 'Transitions to role create route on card action link click');
assert.true(
isURL('roles/create', this.backend),
'Transitions to role create route on card action link click'
);
});
test('it should transition to create library route on card action link click', async function (assert) {
ldapMirageScenario(this.server);
await visitURL('overview');
await this.mountAndConfig(this.backend);
await visitURL('overview', this.backend);
await click('[data-test-overview-card="Libraries"] a');
assert.true(isURL('libraries/create'), 'Transitions to library create route on card action link click');
assert.true(
isURL('libraries/create', this.backend),
'Transitions to library create route on card action link click'
);
});
test('it should transition to role credentials route on generate credentials action', async function (assert) {
ldapMirageScenario(this.server);
await visitURL('overview');
await this.mountAndConfig(this.backend);
await visitURL('overview', this.backend);
await selectChoose('.search-select', 'static-role');
await click('[data-test-generate-credential-button]');
assert.true(
isURL('roles/static/static-role/credentials'),
isURL('roles/static/static-role/credentials', this.backend),
'Transitions to role credentials route on generate credentials action'
);
await click('[data-test-breadcrumb="ldap-test"] a');
await click(`[data-test-breadcrumb="${this.backend}"] a`);
await selectChoose('.search-select', 'dynamic-role');
await click('[data-test-generate-credential-button]');
assert.true(
isURL('roles/dynamic/dynamic-role/credentials'),
isURL('roles/dynamic/dynamic-role/credentials', this.backend),
'Transitions to role credentials route on generate credentials action'
);
});

View File

@@ -6,11 +6,14 @@
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { v4 as uuidv4 } from 'uuid';
import ldapMirageScenario from 'vault/mirage/scenarios/ldap';
import ldapHandlers from 'vault/mirage/handlers/ldap';
import authPage from 'vault/tests/pages/auth';
import { click, fillIn } from '@ember/test-helpers';
import { click, fillIn, waitFor } from '@ember/test-helpers';
import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands';
module('Acceptance | ldap | roles', function (hooks) {
setupApplicationTest(hooks);
@@ -19,26 +22,39 @@ module('Acceptance | ldap | roles', function (hooks) {
hooks.beforeEach(async function () {
ldapHandlers(this.server);
ldapMirageScenario(this.server);
this.backend = `ldap-test-${uuidv4()}`;
await authPage.login();
return visitURL('roles');
// mount & configure
await runCmd([
mountEngineCmd('ldap', this.backend),
`write ${this.backend}/config binddn=foo bindpass=bar url=http://localhost:8208`,
]);
return visitURL('roles', this.backend);
});
hooks.afterEach(async function () {
await runCmd(deleteEngineCmd(this.backend));
});
test('it should transition to create role route on toolbar link click', async function (assert) {
await click('[data-test-toolbar-action="role"]');
assert.true(isURL('roles/create'), 'Transitions to role create route on toolbar link click');
assert.true(
isURL('roles/create', this.backend),
'Transitions to role create route on toolbar link click'
);
});
test('it should transition to role details route on list item click', async function (assert) {
await click('[data-test-list-item-link]:nth-of-type(1) a');
assert.true(
isURL('roles/dynamic/dynamic-role/details'),
isURL('roles/dynamic/dynamic-role/details', this.backend),
'Transitions to role details route on list item click'
);
await click('[data-test-breadcrumb="roles"] a');
await click('[data-test-list-item-link]:nth-of-type(2) a');
assert.true(
isURL('roles/static/static-role/details'),
isURL('roles/static/static-role/details', this.backend),
'Transitions to role details route on list item click'
);
});
@@ -51,7 +67,7 @@ module('Acceptance | ldap | roles', function (hooks) {
await click(`[data-test-${action}]`);
const uri = action === 'get-creds' ? 'credentials' : action;
assert.true(
isURL(`roles/dynamic/dynamic-role/${uri}`),
isURL(`roles/dynamic/dynamic-role/${uri}`, this.backend),
`Transitions to ${uri} route on list item action menu click`
);
await click('[data-test-breadcrumb="roles"] a');
@@ -62,13 +78,16 @@ module('Acceptance | ldap | roles', function (hooks) {
await click('[data-test-list-item-link]:nth-of-type(1) a');
await click('[data-test-get-credentials]');
assert.true(
isURL('roles/dynamic/dynamic-role/credentials'),
isURL('roles/dynamic/dynamic-role/credentials', this.backend),
'Transitions to credentials route from toolbar link'
);
await click('[data-test-breadcrumb="dynamic-role"] a');
await click('[data-test-edit]');
assert.true(isURL('roles/dynamic/dynamic-role/edit'), 'Transitions to edit route from toolbar link');
assert.true(
isURL('roles/dynamic/dynamic-role/edit', this.backend),
'Transitions to edit route from toolbar link'
);
});
test('it should clear roles page filter value on route exit', async function (assert) {
@@ -76,6 +95,7 @@ module('Acceptance | ldap | roles', function (hooks) {
assert
.dom('[data-test-filter-input]')
.hasValue('foo', 'Roles page filter value set after model refresh and rerender');
await waitFor(GENERAL.emptyStateTitle);
await click('[data-test-tab="libraries"]');
await click('[data-test-tab="roles"]');
assert.dom('[data-test-filter-input]').hasNoValue('Roles page filter value cleared on route exit');

View File

@@ -28,13 +28,13 @@ export const generateBreadcrumbs = (backend, childRoute) => {
return breadcrumbs;
};
const baseURL = '/vault/secrets/ldap-test/ldap/';
const baseURL = (backend) => `/vault/secrets/${backend}/ldap/`;
const stripLeadingSlash = (uri) => (uri.charAt(0) === '/' ? uri.slice(1) : uri);
export const isURL = (uri) => {
return currentURL() === `${baseURL}${stripLeadingSlash(uri)}`;
export const isURL = (uri, backend = 'ldap-test') => {
return currentURL() === `${baseURL(backend)}${stripLeadingSlash(uri)}`;
};
export const visitURL = (uri) => {
return visit(`${baseURL}${stripLeadingSlash(uri)}`);
export const visitURL = (uri, backend = 'ldap-test') => {
return visit(`${baseURL(backend)}${stripLeadingSlash(uri)}`);
};

View File

@@ -77,6 +77,9 @@ module('Integration | Component | ldap | Page::Library::Details::Accounts', func
assert
.dom('[data-test-accounts-code-block] code')
.hasText('vault lease renew ad/library/test-library/check-out/:lease_id', 'Renew cli command renders');
.hasText(
'vault lease renew ldap-test/library/test-library/check-out/:lease_id',
'Renew cli command renders with backend path'
);
});
});