diff --git a/changelog/_14731.txt b/changelog/_14731.txt new file mode 100644 index 0000000000..f00fb68927 --- /dev/null +++ b/changelog/_14731.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix LDAP hierarchical role navigation in UI +``` \ No newline at end of file diff --git a/ui/lib/ldap/addon/routes/roles.ts b/ui/lib/ldap/addon/routes/roles.ts index 029b2cb243..3798fe7c9c 100644 --- a/ui/lib/ldap/addon/routes/roles.ts +++ b/ui/lib/ldap/addon/routes/roles.ts @@ -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), diff --git a/ui/tests/acceptance/secrets/backend/ldap/roles-test.js b/ui/tests/acceptance/secrets/backend/ldap/roles-test.js index 8d61bb3997..3f7c2a1df7 100644 --- a/ui/tests/acceptance/secrets/backend/ldap/roles-test.js +++ b/ui/tests/acceptance/secrets/backend/ldap/roles-test.js @@ -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(); + }); }); });