LDAP: the group members were not successfully synchronized to the role #5377

Open
opened 2026-02-05 10:00:59 +03:00 by OVERLORD · 4 comments
Owner

Originally created by @LoongKK on GitHub (Jul 28, 2025).

Attempted Debugging

  • I have read the debugging page

Searched GitHub Issues

  • I have searched GitHub for the issue.

Describe the Scenario

debug:
{
"details_from_ldap": {
"memberof": {
"count": 1,
"0": "CN=信息部,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com"
},
"0": "memberof",
"count": 1,
"dn": "CN=myname,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com"
},
"parsed_direct_user_groups": [
"CN=信息部,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com"
],
"parsed_recursive_user_groups": [
"CN=信息部,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com"
],
"parsed_resulting_group_names": [
"\E4\BF\A1\E6\81\AF\E9\83\A8"
]
}

"\E4\BF\A1\E6\81\AF\E9\83\A8" means "信息部" (Chinese)

Set the role:External Authentication IDs ——>信息部
Image

However, it was not effective as the group members were not successfully synchronized to the role

Exact BookStack Version

v25.05.2

Log Content


Hosting Environment

Docker-compose

Originally created by @LoongKK on GitHub (Jul 28, 2025). ### Attempted Debugging - [x] I have read the debugging page ### Searched GitHub Issues - [x] I have searched GitHub for the issue. ### Describe the Scenario debug: { "details_from_ldap": { "memberof": { "count": 1, "0": "CN=信息部,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com" }, "0": "memberof", "count": 1, "dn": "CN=myname,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com" }, "parsed_direct_user_groups": [ "CN=信息部,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com" ], "parsed_recursive_user_groups": [ "CN=信息部,OU=信息部,OU=xxx,OU=xxx,DC=xxx,DC=com" ], "parsed_resulting_group_names": [ "\\E4\\BF\\A1\\E6\\81\\AF\\E9\\83\\A8" ] } "\\E4\\BF\\A1\\E6\\81\\AF\\E9\\83\\A8" means "信息部" (Chinese) Set the role:External Authentication IDs ——>信息部 <img width="830" height="392" alt="Image" src="https://github.com/user-attachments/assets/a882a36e-007d-47c4-962f-68fbbfcf20c2" /> However, it was not effective as the group members were not successfully synchronized to the role ### Exact BookStack Version v25.05.2 ### Log Content ```text ``` ### Hosting Environment Docker-compose
OVERLORD added the 🐕 Support📖 Docs Update labels 2026-02-05 10:00:59 +03:00
Author
Owner

@LoongKK commented on GitHub (Jul 28, 2025):

Hi , I encountered the issue.

I found that the group names in parsed_resulting_group_names were being passed in an escaped URL-encoded format like \E4\BF\A1, which represents UTF-8 characters (Chinese).

I was able to fix it by decoding the strings like this:

$decodedGroupNames = array_map(function($name) {
    return urldecode(str_replace('\\', '%', $name));
}, $groupNames);

Then use $decodedGroupNames. This works perfectly for both Chinese and English group names, without any side effects.

Hope this helps others facing the same issue. Thanks!

edit /app/Access/GroupSyncService.php:
protected function roleMatchesGroupNames(Role $role, array $groupNames): bool
{
if ($role->external_auth_id) {
return $this->externalIdMatchesGroupNames($role->external_auth_id, $groupNames);
}

    $roleName = str_replace(' ', '-', trim(strtolower($role->display_name)));                                                                         
                                                                                                                                                      
    //return in_array($roleName, $groupNames);                                                                                                        
    
    $decodedGroupNames = array_map(function($name) {return urldecode(str_replace('\\', '%', $name));}, $groupNames);                                  
                                                                                                                                                      
    return in_array($roleName, $decodedGroupNames);                                                                                                   
}                                                                                                                                                     
                                                                                                                                                      
                                                                                                                                              
protected function externalIdMatchesGroupNames(string $externalId, array $groupNames): bool                                                           
{                                                                                                                                                     
    foreach ($this->parseRoleExternalAuthId($externalId) as $externalAuthId) {                                                                        
        
        /*
        if (in_array($externalAuthId, $groupNames)) {                                                                                               
            return true;                                                                                                                              
        }
        */
                                                                                                                                       
        $decodedGroupNames = array_map(function($name) {return urldecode(str_replace('\\', '%', $name));}, $groupNames);                              
                                                                                                                                                      
        if (in_array($externalAuthId, $decodedGroupNames)){                                                                                           
            return true;                                                                                                                              
        }                                                                                                                                             
    }                                                                                                                                                 
                                                                                                                                                      
    return false;                                                                                                                                     
}                        
@LoongKK commented on GitHub (Jul 28, 2025): Hi , I encountered the issue. I found that the group names in `parsed_resulting_group_names` were being passed in an escaped URL-encoded format like `\E4\BF\A1`, which represents UTF-8 characters (Chinese). I was able to fix it by decoding the strings like this: ```php $decodedGroupNames = array_map(function($name) { return urldecode(str_replace('\\', '%', $name)); }, $groupNames); ``` Then use $decodedGroupNames. This works perfectly for both Chinese and English group names, without any side effects. Hope this helps others facing the same issue. Thanks! edit /app/Access/GroupSyncService.php: protected function roleMatchesGroupNames(Role $role, array $groupNames): bool { if ($role->external_auth_id) { return $this->externalIdMatchesGroupNames($role->external_auth_id, $groupNames); } $roleName = str_replace(' ', '-', trim(strtolower($role->display_name))); //return in_array($roleName, $groupNames); $decodedGroupNames = array_map(function($name) {return urldecode(str_replace('\\', '%', $name));}, $groupNames); return in_array($roleName, $decodedGroupNames); } protected function externalIdMatchesGroupNames(string $externalId, array $groupNames): bool { foreach ($this->parseRoleExternalAuthId($externalId) as $externalAuthId) { /* if (in_array($externalAuthId, $groupNames)) { return true; } */ $decodedGroupNames = array_map(function($name) {return urldecode(str_replace('\\', '%', $name));}, $groupNames); if (in_array($externalAuthId, $decodedGroupNames)){ return true; } } return false; }
Author
Owner

@ssddanbrown commented on GitHub (Jul 28, 2025):

Hi @LoongKK,
As an alternative option without requiring app modifications you should be able to use the escaped name in the "External Authentication IDs" role field to match up with such roles.

I've labelled this issue as something to update in our docs since this has come up a few times previously.

It wouldn't be something I'd look to auto-escape, since the specific escape logic could be dependant on the LDAP server in use and since such changes would break (or at least complicate) existing uses of escaped group names.

@ssddanbrown commented on GitHub (Jul 28, 2025): Hi @LoongKK, As an alternative option without requiring app modifications you should be able to use the escaped name in the "External Authentication IDs" role field to match up with such roles. I've labelled this issue as something to update in our docs since this has come up a few times previously. It wouldn't be something I'd look to auto-escape, since the specific escape logic could be dependant on the LDAP server in use and since such changes would break (or at least complicate) existing uses of escaped group names.
Author
Owner

@LoongKK commented on GitHub (Jul 29, 2025):

hi @ssddanbrown ,I've also found an issue: if both CN=Group1,OU=deptA,OU=xxx,OU=xxx,DC=xxx,DC=com and CN=Group1,OU=deptB,OU=xxx,OU=xxx,DC=xxx,DC=com exist simultaneously, using the Role's ExternalAuthId (i.e., the CN name) will incorrectly map these two groups to a single role. Is there any way to avoid this error, or can we use the full value of 'memberOf' for matching instead?

@LoongKK commented on GitHub (Jul 29, 2025): hi @ssddanbrown ,I've also found an issue: if both CN=Group1,OU=deptA,OU=xxx,OU=xxx,DC=xxx,DC=com and CN=Group1,OU=deptB,OU=xxx,OU=xxx,DC=xxx,DC=com exist simultaneously, using the Role's ExternalAuthId (i.e., the CN name) will incorrectly map these two groups to a single role. Is there any way to avoid this error, or can we use the full value of 'memberOf' for matching instead?
Author
Owner

@LoongKK commented on GitHub (Jul 29, 2025):

hi @ssddanbrown ,I've also found an issue: if both CN=Group1,OU=deptA,OU=xxx,OU=xxx,DC=xxx,DC=com and CN=Group1,OU=deptB,OU=xxx,OU=xxx,DC=xxx,DC=com exist simultaneously, using the Role's ExternalAuthId (i.e., the CN name) will incorrectly map these two groups to a single role. Is there any way to avoid this error, or can we use the full value of 'memberOf' for matching instead?嗨,我还发现了一个问题:如果 CN=Group1,OU=deptA,OU=xxx,OU=xxx,DC=com 和 CN=Group1,OU=deptB,OU=xxx,OU=xxx,DC=xxx,DC=com 同时存在,使用角色的 ExternalAuthId(即 CN 名称)将错误地将这两个组映射到单个角色。有什么方法可以避免此错误,或者我们可以使用'memberOf'的完整值进行匹配吗?

/app/www/app/Access/LdapService.php [Modified]
//$names[] = $exploded[0];
$filtered = array_diff_key($exploded, ['count' => '', 'dn' => '']);
$joinedString = implode('-', $filtered);
$names[] = $joinedString;

Then, I can use Group1-deptA-xxx-xxx-xxx-com as the ExternalAuthId

@LoongKK commented on GitHub (Jul 29, 2025): > hi [@ssddanbrown](https://github.com/ssddanbrown) ,I've also found an issue: if both CN=Group1,OU=deptA,OU=xxx,OU=xxx,DC=xxx,DC=com and CN=Group1,OU=deptB,OU=xxx,OU=xxx,DC=xxx,DC=com exist simultaneously, using the Role's ExternalAuthId (i.e., the CN name) will incorrectly map these two groups to a single role. Is there any way to avoid this error, or can we use the full value of 'memberOf' for matching instead?嗨,我还发现了一个问题:如果 CN=Group1,OU=deptA,OU=xxx,OU=xxx,DC=com 和 CN=Group1,OU=deptB,OU=xxx,OU=xxx,DC=xxx,DC=com 同时存在,使用角色的 ExternalAuthId(即 CN 名称)将错误地将这两个组映射到单个角色。有什么方法可以避免此错误,或者我们可以使用'memberOf'的完整值进行匹配吗? /app/www/app/Access/LdapService.php [Modified] //$names[] = $exploded[0]; $filtered = array_diff_key($exploded, ['count' => '', 'dn' => '']); $joinedString = implode('-', $filtered); $names[] = $joinedString; Then, I can use Group1-deptA-xxx-xxx-xxx-com as the ExternalAuthId
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/BookStack#5377