Skip to content

Ldap extractGroupNamesFromLdapGroupDns checks for empty results incorrectly #6088

@SamVanheer

Description

@SamVanheer

Describe the Bug

The function extractGroupNamesFromLdapGroupDns checks the exploded DN array size incorrectly. If $this->ldap->explodeDn returns an array with no DN components then an exception is thrown:

Details
[2026-04-08 09:16:07] production.ERROR: Undefined array key 0 {"exception":"[object] (ErrorException(code: 0): Undefined array key 0 at /data/var/www/bookstack/app/Access/LdapService.php:374)
[stacktrace]
#0 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php(258): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError()
#1 /data/var/www/bookstack/app/Access/LdapService.php(374): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->Illuminate\\Foundation\\Bootstrap\\{closure}()
#2 /data/var/www/bookstack/app/Access/LdapService.php(350): BookStack\\Access\\LdapService->extractGroupNamesFromLdapGroupDns()
#3 /data/var/www/bookstack/app/Access/LdapService.php(460): BookStack\\Access\\LdapService->getUserGroups()
#4 /data/var/www/bookstack/app/Access/Guards/LdapSessionGuard.php(88): BookStack\\Access\\LdapService->syncGroups()
#5 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php(334): BookStack\\Access\\Guards\\LdapSessionGuard->attempt()
#6 /data/var/www/bookstack/app/Access/LoginService.php(165): Illuminate\\Auth\\AuthManager->__call()
#7 /data/var/www/bookstack/app/Access/Controllers/LoginController.php(134): BookStack\\Access\\LoginService->attempt()
#8 /data/var/www/bookstack/app/Access/Controllers/LoginController.php(74): BookStack\\Access\\Controllers\\LoginController->attemptLogin()
#9 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): BookStack\\Access\\Controllers\\LoginController->login()
#10 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()
#11 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Route.php(265): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#12 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Route.php(211): Illuminate\\Routing\\Route->runController()
#13 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Router.php(822): Illuminate\\Routing\\Route->run()
#14 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#15 /data/var/www/bookstack/app/Http/Middleware/CheckGuard.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#16 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\CheckGuard->handle()
#17 /data/var/www/bookstack/app/Http/Middleware/RedirectIfAuthenticated.php(28): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#18 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\RedirectIfAuthenticated->handle()
#19 /data/var/www/bookstack/app/Http/Middleware/Localization.php(32): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#20 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\Localization->handle()
#21 /data/var/www/bookstack/app/Http/Middleware/RunThemeActions.php(26): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#22 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\RunThemeActions->handle()
#23 /data/var/www/bookstack/app/Http/Middleware/CheckEmailConfirmed.php(47): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#24 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\CheckEmailConfirmed->handle()
#25 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(87): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#26 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()
#27 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(48): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#28 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()
#29 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(120): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#30 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(63): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#31 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Session\\Middleware\\StartSession->handle()
#32 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(36): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#33 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
#34 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(74): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#35 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
#36 /data/var/www/bookstack/app/Http/Middleware/ApplyCspRules.php(33): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#37 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\ApplyCspRules->handle()
#38 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(137): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#39 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Router.php(821): Illuminate\\Pipeline\\Pipeline->then()
#40 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Router.php(800): Illuminate\\Routing\\Router->runRouteWithinStack()
#41 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Router.php(764): Illuminate\\Routing\\Router->runRoute()
#42 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Routing/Router.php(753): Illuminate\\Routing\\Router->dispatchToRoute()
#43 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()
#44 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#45 /data/var/www/bookstack/app/Http/Middleware/PreventResponseCaching.php(28): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#46 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\PreventResponseCaching->handle()
#47 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(58): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#48 /data/var/www/bookstack/app/Http/Middleware/TrustProxies.php(41): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#49 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): BookStack\\Http\\Middleware\\TrustProxies->handle()
#50 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#51 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#52 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#53 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#54 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Http\\Middleware\\ValidatePostSize->handle()
#55 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(109): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#56 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#57 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(137): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#58 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()
#59 /data/var/www/bookstack/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#60 /data/var/www/bookstack/public/index.php(23): Illuminate\\Foundation\\Http\\Kernel->handle()
#61 {main}
"} 

This is because the array returned by explodeDn is not an empty array; https://www.php.net/manual/en/function.ldap-explode-dn.php#refsect1-function.ldap-explode-dn-returnvalues

The first element in the array has count key and represents the number of returned values, next elements are numerically indexed DN components.

The correct way to check if there are no elements in the array is this: $exploded['count'] > 0. With this change in place the user is able to log in.

This happens during user login and prevents the user from successfully logging in. In our case this is because the user is a member of an AD group whose name contains a Unicode character ë. explodeDn can't handle this and returns an empty array (with count set to 0).

I'd recommend adding a test case for this.

Steps to Reproduce

  1. Use LDAP and have a user with a group name that contains Unicode characters like ë, which fails to parse and returns an empty array
  2. Try to log in with this user
  3. The above problem occurs

Expected Behaviour

Bookstack ignores the empty array and continues to process user login.

Screenshots or Additional Context

No response

Browser Details

Firefox 149

Exact BookStack Version

v26.03.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions