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
- Use LDAP and have a user with a group name that contains Unicode characters like ë, which fails to parse and returns an empty array
- Try to log in with this user
- 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
Describe the Bug
The function
extractGroupNamesFromLdapGroupDnschecks the exploded DN array size incorrectly. If$this->ldap->explodeDnreturns an array with no DN components then an exception is thrown:Details
This is because the array returned by
explodeDnis not an empty array; https://www.php.net/manual/en/function.ldap-explode-dn.php#refsect1-function.ldap-explode-dn-returnvaluesThe 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
ë.explodeDncan't handle this and returns an empty array (withcountset to0).I'd recommend adding a test case for this.
Steps to Reproduce
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