[VAULT-44669] Address LDAP hierarchical role bug (#14731) (#14739) (#14740)

* [VAULT-44669] Address LDAP hierarchical role bug

* add test coverage

* add changelog

Co-authored-by: Shannon Roberts (Beagin) <beagins@users.noreply.github.com>
This commit is contained in:
Vault Automation 2026-05-13 09:50:53 -06:00 committed by GitHub
parent 5f065418bd
commit 772304129f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 19 deletions

3
changelog/_14731.txt Normal file
View file

@ -0,0 +1,3 @@
```release-note:bug
ui: Fix LDAP hierarchical role navigation in UI
```

View file

@ -5,21 +5,21 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { paginate, PaginateOptions } from 'core/utils/paginate-list';
import sortObjects from 'vault/utils/sort-objects';
import { fetchRoleCapabilities } from 'ldap/utils/capabilities-helper';
import {
SecretsApiLdapListStaticRolesListEnum,
SecretsApiLdapListDynamicRolesListEnum,
SecretsApiLdapListStaticRolePathListEnum,
SecretsApiLdapListRolePathListEnum,
SecretsApiLdapListStaticRolePathListEnum,
SecretsApiLdapListStaticRolesListEnum,
} from '@hashicorp/vault-client-typescript';
import { paginate, PaginateOptions } from 'core/utils/paginate-list';
import { fetchRoleCapabilities } from 'ldap/utils/capabilities-helper';
import sortObjects from 'vault/utils/sort-objects';
import type ApiService from 'vault/services/api';
import type SecretMountPath from 'vault/services/secret-mount-path';
import type FlashMessageService from 'ember-cli-flash/services/flash-messages';
import type CapabilitiesService from 'vault/services/capabilities';
import type { LdapRole } from 'vault/secrets/ldap';
import type ApiService from 'vault/services/api';
import type CapabilitiesService from 'vault/services/capabilities';
import type SecretMountPath from 'vault/services/secret-mount-path';
// Base class for roles/index and roles/subdirectory routes
export default class LdapRolesRoute extends Route {
@ -41,12 +41,12 @@ export default class LdapRolesRoute extends Route {
subType === 'static'
? [
this.api.secrets.ldapListStaticRolePath(
currentPath,
path,
currentPath,
SecretsApiLdapListStaticRolePathListEnum.TRUE
),
]
: [this.api.secrets.ldapListRolePath(currentPath, path, SecretsApiLdapListRolePathListEnum.TRUE)];
: [this.api.secrets.ldapListRolePath(path, currentPath, SecretsApiLdapListRolePathListEnum.TRUE)];
} else {
requests = [
this.api.secrets.ldapListStaticRoles(currentPath, SecretsApiLdapListStaticRolesListEnum.TRUE),

View file

@ -3,17 +3,18 @@
* SPDX-License-Identifier: BUSL-1.1
*/
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 { login } from 'vault/tests/helpers/auth/auth-helpers';
import { click, fillIn, waitFor } from '@ember/test-helpers';
import { assertURL, isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { setupApplicationTest } from 'ember-qunit';
import { module, test } from 'qunit';
import sinon from 'sinon';
import { v4 as uuidv4 } from 'uuid';
import ldapHandlers from 'vault/mirage/handlers/ldap';
import ldapMirageScenario from 'vault/mirage/scenarios/ldap';
import { login } from 'vault/tests/helpers/auth/auth-helpers';
import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { assertURL, isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers';
import { LDAP_SELECTORS } from 'vault/tests/helpers/ldap/ldap-selectors';
module('Acceptance | ldap | roles', function (hooks) {
@ -166,5 +167,37 @@ module('Acceptance | ldap | roles', function (hooks) {
await click('[data-test-tab="roles"]');
assert.dom('[data-test-filter-input]').hasNoValue('Roles page filter value cleared on route exit');
});
test('it calls API with correct parameter order for hierarchical paths', async function (assert) {
// Get the API service and stub the SDK methods
const owner = this.owner;
const apiService = owner.lookup('service:api');
const ldapListStaticRolePathStub = sinon.stub(apiService.secrets, 'ldapListStaticRolePath');
const ldapListRolePathStub = sinon.stub(apiService.secrets, 'ldapListRolePath');
// Configure stubs to return empty lists
ldapListStaticRolePathStub.resolves({ keys: [] });
ldapListRolePathStub.resolves({ keys: [] });
// Navigate to static role subdirectory
await visitURL('roles/static/subdirectory/admin/', this.backend);
// Verify static role API was called with correct parameter order
assert.true(ldapListStaticRolePathStub.calledOnce, 'ldapListStaticRolePath was called');
const [staticPath, staticMount] = ldapListStaticRolePathStub.firstCall.args;
assert.strictEqual(staticPath, 'admin/', 'First parameter is the hierarchical path');
assert.strictEqual(staticMount, this.backend, 'Second parameter is the mount path');
// Navigate to dynamic role subdirectory
await visitURL('roles/dynamic/subdirectory/admin/', this.backend);
// Verify dynamic role API was called with correct parameter order
const [dynamicPath] = ldapListRolePathStub.firstCall.args;
assert.strictEqual(dynamicPath, 'admin/', 'First parameter is the hierarchical path for dynamic roles');
// Restore stubs
ldapListStaticRolePathStub.restore();
ldapListRolePathStub.restore();
});
});
});