ldap_write_support 1.13.0 and Nextcloud Hub 10 (31.0.1) do not work properly when writing and modifying groups.
How to use GitHub
- Please use the 👍 reaction to show that you are affected by the same issue.
- Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
- Subscribe to receive notifications on status change and new comments.
Steps to reproduce
- Install and configure Nextcloud and LDAP
- Configure Users and Groups on LDAP
- Modify the groups of one user
Expected behaviour
By changing the groups the user belongs to, the change is recorded on LDAP
Actual behaviour
When modifying user groups the change is not recorded on LDAP
Server configuration
<?php
$CONFIG = array (
'datadirectory' => '/data',
'instanceid' => '************',
'passwordsalt' => '/*********************',
'secret' => '***************************************',
'trusted_domains' =>
array (
0 => '192.168.1.100:70',
),
'dbtype' => 'mysql',
'version' => '31.0.1.2',
'overwrite.cli.url' => 'http://192.168.1.100:70',
'dbname' => 'nextcloud',
'dbhost' => 'db',
'dbport' => '',
'dbtableprefix' => 'oc_',
'mysql.utf8mb4' => true,
'dbuser' => '*******',
'dbpassword' => '********',
'installed' => true,
'ldapProviderFactory' => 'OCA\\User_LDAP\\LDAPProviderFactory',
'app_install_overwrite' =>
array (
0 => 'wopi',
),
'loglevel' => 2,
'maintenance' => false,
'memcache.local' => '\\OC\\Memcache\\APCu',
'filelocking.enabled' => true,
'memcache.locking' => '\\OC\\Memcache\\APCu',
'upgrade.disable-web' => true,
);
Web server: Apache/Nginx Ngnix Version 2024/05/27
Database: MySQL/Maria/SQLite/PostgreSQL MariaDB 11.4.5
PHP version: 8.1/8.2/8.3 PHP 8.3.17
Nextcloud version: (see Nextcloud admin page) Nextcloud Hub 10 (31.0.1)
List of activated apps
occ app:listEnabled:
- activity: 4.0.0
- app_api: 5.0.2
- bruteforcesettings: 4.0.0
- calendar: 5.1.3
- circles: 31.0.0
- cloud_federation_api: 1.14.0
- comments: 1.21.0
- contacts: 7.0.4
- contactsinteraction: 1.12.0
- dashboard: 7.11.0
- dav: 1.33.0
- federatedfilesharing: 1.21.0
- federation: 1.21.0
- files: 2.3.1
- files_downloadlimit: 4.0.0
- files_external: 1.23.0
- files_pdfviewer: 4.0.0
- files_reminders: 1.4.0
- files_sharing: 1.23.1
- files_trashbin: 1.21.0
- files_versions: 1.24.0
- firstrunwizard: 4.0.0
- ldap_write_support: 1.13.0
- logreader: 4.0.0
- lookup_server_connector: 1.19.0
- mail: 4.2.6
- nextcloud_announcements: 3.0.0
- notes: 4.11.0
- notifications: 4.0.0
- oauth2: 1.19.1
- password_policy: 3.0.0
- photos: 4.0.0-dev.1
- privacy: 3.0.0
- profile: 1.0.0
- provisioning_api: 1.21.0
- recommendations: 4.0.0
- related_resources: 2.0.0
- richdocuments: 8.6.2
- serverinfo: 3.0.0
- settings: 1.14.0
- sharebymail: 1.21.0
- spreed: 21.0.1
- support: 3.0.0
- survey_client: 3.0.0
- systemtags: 1.21.1
- text: 5.0.0
- theming: 2.6.1
- twofactor_backupcodes: 1.20.0
- updatenotification: 1.21.0
- user_ldap: 1.22.0
- user_status: 1.11.0
- viewer: 4.0.0
- weather_status: 1.11.0
- webhook_listeners: 1.2.0
- workflowengine: 2.13.0
Disabled:
- admin_audit: 1.21.0
- encryption: 2.19.0
- suspicious_login: 9.0.1
- twofactor_nextcloud_notification: 5.0.0
- twofactor_totp: 13.0.0-dev.0
Nextcloud configuration
If you have access to your command line run e.g.:
sudo -u www-data php occ config:list system
from within your Nextcloud installation folder
occ config:list system { "system": { "datadirectory": "REMOVED SENSITIVE VALUE", "instanceid": "REMOVED SENSITIVE VALUE", "passwordsalt": "REMOVED SENSITIVE VALUE", "secret": "REMOVED SENSITIVE VALUE", "trusted_domains": [ "192.168.1.100:70" ], "dbtype": "mysql", "version": "31.0.1.2", "overwrite.cli.url": "http://192.168.1.100:70", "dbname": "REMOVED SENSITIVE VALUE", "dbhost": "REMOVED SENSITIVE VALUE", "dbport": "", "dbtableprefix": "oc_", "mysql.utf8mb4": true, "dbuser": "REMOVED SENSITIVE VALUE", "dbpassword": "REMOVED SENSITIVE VALUE", "installed": true, "ldapProviderFactory": "OCA\User_LDAP\LDAPProviderFactory", "app_install_overwrite": [ "wopi" ], "loglevel": 2, "maintenance": false, "memcache.local": "\OC\Memcache\APCu", "filelocking.enabled": true, "memcache.locking": "\OC\Memcache\APCu", "upgrade.disable-web": true } }
Browser
Browser name: Firefox/Chrome/Safari/… Chrome Browser version: 124/125/… Versione 134.0.6998.89 Operating system: Windows/Ubuntu/Mac/… Windows 11
Browser log
Insert your browser log here, this could for example include:
a) The javascript console log
b) The network log
c) ...
Hi @russohub, I was having this problem too. I solved my case, but I had to dig around in the plugin's code to do it.
tl;dr
Try Settings -> LDAP/AD Integration -> Advanced -> Directory settings -> Group-Member association = "Member (AD)"
Click https://your-domain.tld/settings/admin/ldap and choose this:
🦖🛠️🏗️🦕🔧🚧👷♂️🧱🛠️🦖🔨👷♀️🦕🔩🚜🔧🦕🧰🛠️🦖🏗️👷♂️🦕⚙️🔧🚧🛠️🦖🏗️🔨👷♀️🦕🧱🔩🛠️🚜🦖🧰🔧🦕🏗️👷♂️🦖🛠️⚙️🔨🦕🚧🧱🔧🦖👷♀️🧰🔩🛠️🦕🏗️🦖🔧🚜🛠️🧱🦕👷♂️🔨⚙️🦖🔩👷♀️🛠️🧰🚧🦕🏗️🦖🔧🛠️🧱🔨🦕👷♂️⚙️🔩🦖
Versions
I'm on:
- Debian 12 (bookworm)
slapd2.5.13+dfsg-5- Nextcloud Hub 10 (31.0.5) installed via tarball.
user_ldap1.22.0 enabled via app storeldap_write_support1.13.0 installed via app store
Setup
I enabled user_ldap and ldap_write_support in my NextCloud settings.
The slapd .deb installer prompted me to set an admin password and set up the base DN based on my hostname dc=files,dc=tilde,dc=xyz. I gave https://files.tilde.xyz/settings/admin/ldap the LDAP admin account cn=admin,dc=files,dc=tilde,dc=xyz and the password I generated for it.
I didn't do any special configuration otherwise! Well, okay, I did, but none of it was relevant in the end and I apt-get autopurge slapd && apt-get install slapd to make sure of it.
Debugging
I found it was easier to debug using occ, because then I don't have stray logs or worries data getting mangled by JavaScript/cookies/reverse proxies/etc I also found it easier to debug as root. If it helps you to replicate my method keep those in mind!
With user_ldap and ldap_write_support enabled
I can add a user to LDAP through NextCloud. In fact I can only create LDAP users when ldap_write_support is enabled (but I can still login as non-LDAP aka "Database" users created before before ldap_write_support).
root@files:/var/www/nextcloud# sudo -u www-data php occ user:add User1
Enter password:
Confirm password:
The account "User1" was created successfully
root@files:/var/www/nextcloud# sudo -u www-data php occ user:list -i -l User1
- User1:
- user_id: User1
- display_name: User1
- email:
- cloud_id: [email protected]
- enabled: true
- groups:
- quota: 10 MB
- first_seen: never
- last_seen: never
- user_directory: /var/www/nextcloud/data/User1
- backend: LDAP
Nextcloud thinks the user is in LDAP, and indeed I can find that user with slapcat (server-side) and ldapsearch (client-side)
root@files:/var/www/nextcloud# slapcat | grep -C 5 User1
modifiersName: cn=admin,dc=files,dc=tilde,dc=xyz
modifyTimestamp: 20250527022723Z
dn: uid=user1,dc=files,dc=tilde,dc=xyz
objectClass: inetOrgPerson
uid: User1
displayName: User1
cn: User1
sn: User1
structuralObjectClass: inetOrgPerson
entryUUID: 89fa6f62-cf68-103f-89c4-1b96b71daffb
creatorsName: cn=admin,dc=files,dc=tilde,dc=xyz
createTimestamp: 20250527170530Z
userPassword:: e1NTSEF9ZnZNbVlPT21xK3QwWUY2T2JXZWFnaW9qUW13UitxRUk=
root@files:/var/www/nextcloud# ldapsearch -LLL -x -b 'dc=files,dc=tilde,dc=xyz' '(uid=user1)'
dn: uid=user1,dc=files,dc=tilde,dc=xyz
objectClass: inetOrgPerson
uid: User1
displayName: User1
cn: User1
sn: User1
Similarly I can create LDAP (and only LDAP) groups:
root@files:/var/www/nextcloud# sudo -u www-data php occ group:add Group1
Created group "Group1"
root@files:/var/www/nextcloud# sudo -u www-data php occ group:info Group1
- groupID: Group1
- displayName: Group1
- backends:
- LDAP
And I can see it slapcat and ldapsearch
root@files:/var/www/nextcloud# slapcat | grep -C 5 Group1
modifyTimestamp: 20250527170530Z
dn: cn=group1,dc=files,dc=tilde,dc=xyz
objectClass: groupOfNames
objectClass: top
cn: Group1
member:
structuralObjectClass: groupOfNames
entryUUID: 54b08930-cf69-103f-89c5-1b96b71daffb
creatorsName: cn=admin,dc=files,dc=tilde,dc=xyz
createTimestamp: 20250527171110Z
root@files:/var/www/nextcloud# ldapsearch -LLL -x -b 'dc=files,dc=tilde,dc=xyz' '(cn=Group1)'
dn: cn=group1,dc=files,dc=tilde,dc=xyz
objectClass: groupOfNames
objectClass: top
cn: Group1
member:
But if I try to use occ group:adduser it 'succeeds' but hasn't actually edited the group:
root@files:/var/www/nextcloud# sudo -u www-data php occ group:adduser Group1 User1 ; echo $?
0 # -> it reports 'success'
root@files:/var/www/nextcloud# sudo -u www-data php occ user:list -i -l User1
- User1:
- user_id: User1
- display_name: User1
- email:
- cloud_id: [email protected]
- enabled: true
- groups:
- quota: 10 MB
- first_seen: never
- last_seen: never
- user_directory: /var/www/nextcloud/data/User1
- backend: LDAP
root@files:/var/www/nextcloud# sudo -u www-data php occ group:list -i
- Group1:
- displayName: Group1
- backends:
- LDAP
- users:
For completeness, I tried doing this all in one with user:add instead of group:adduser, but I got the same result
It reports adding the user to the groups:
root@files:/var/www/nextcloud# sudo -u www-data php occ user:add User2 -g Group2 -g Group3 -g Group4
Enter password:
Confirm password:
The account "User2" was created successfully
Created group "Group2"
Account "User2" added to group "Group2"
Created group "Group3"
Account "User2" added to group "Group3"
Created group "Group4"
Account "User2" added to group "Group4"
The groups made it to LDAP, but they're empty:
root@files:/var/www/nextcloud# ldapsearch -LLL -x -b 'dc=files,dc=tilde,dc=xyz' '(cn=Group*)'
dn: cn=group1,dc=files,dc=tilde,dc=xyz
objectClass: groupOfNames
objectClass: top
cn: Group1
member:
dn: cn=group2,dc=files,dc=tilde,dc=xyz
objectClass: groupOfNames
objectClass: top
cn: Group2
member:
dn: cn=group3,dc=files,dc=tilde,dc=xyz
objectClass: groupOfNames
objectClass: top
cn: Group3
member:
dn: cn=group4,dc=files,dc=tilde,dc=xyz
objectClass: groupOfNames
objectClass: top
cn: Group4
member:
NextCloud also sees they're empty and that User2 is in none:
root@files:/var/www/nextcloud# sudo -u www-data php occ user:info User2
- user_id: User2
- display_name: User2
- email:
- cloud_id: [email protected]
- enabled: true
- groups:
- quota: 10 MB
- storage:
- first_seen: never
- last_seen: never
- user_directory: /var/www/nextcloud/data/User2
- backend: LDAP
root@files:/var/www/nextcloud# sudo -u www-data php occ group:list -i
- Group1:
- displayName: Group1
- backends:
- LDAP
- users:
- Group2:
- displayName: Group2
- backends:
- LDAP
- users:
- Group3:
- displayName: Group3
- backends:
- LDAP
- users:
- Group4:
- displayName: Group4
- backends:
- LDAP
- users:
So that makes sense, it's behaving the same. Back to the hunt.
Tracing
I got frustrated with LDAP and gave up, but once I slept it off, woke up, and made some tea, I decided to trace the code. Long story short: I have this patch:
--- /tmp/LDAPGroupManager.php 2025-05-27 13:30:54.928348088 -0400
+++ /var/www/nextcloud/apps/ldap_write_support/lib/LDAPGroupManager.php 2025-05-27 13:30:07.912031077 -0400
@@ -69,6 +69,7 @@
$newGroupDN = "cn=$gid," . $this->ldapConnect->getLDAPBaseGroups()[0];
$newGroupDN = $this->ldapProvider->sanitizeDN([$newGroupDN])[0];
+ $this->logger->error("createGroup: ldap_add($newGroupDN," . print_r($newGroupEntry,true) . ")");
if ($connection && ($ret = ldap_add($connection, $newGroupDN, $newGroupEntry))) {
$message = "Create LDAP group '$gid' ($newGroupDN)";
$this->logger->notice($message, ['app' => Application::APP_ID]);
@@ -91,6 +92,7 @@
$connection = $this->ldapProvider->getGroupLDAPConnection($gid);
$groupDN = $this->ldapProvider->getGroupDN($gid);
+ $this->logger->error("deleteGroup: ldap_delete($groupDN)");
if (!$ret = ldap_delete($connection, $groupDN)) {
$message = 'Unable to delete LDAP Group: ' . $gid;
$this->logger->error($message, ['app' => Application::APP_ID]);
@@ -116,6 +118,7 @@
$groupDN = $this->ldapProvider->getGroupDN($gid);
$entry = [];
+ $this->logger->error("LDAP Group Member Assoc: " . $this->ldapProvider->getLDAPGroupMemberAssoc($gid));
switch ($this->ldapProvider->getLDAPGroupMemberAssoc($gid)) {
case 'memberUid':
$entry['memberuid'] = $uid;
@@ -131,6 +134,7 @@
break;
}
+ $this->logger->error("addToGroup: ldap_mod_add($groupDN," . print_r($entry,true) . ")");
if (!$ret = ldap_mod_add($connection, $groupDN, $entry)) {
$message = 'Unable to add user ' . $uid . ' to group ' . $gid;
$this->logger->error($message, ['app' => Application::APP_ID]);
And then I watched with tail -f /var/www/nextcloud/data/nextcloud.log as I tried php occ group:adduser Group1 User1 again.
nextcloud.log
{"reqId":"9mss2xdtNL7nglX37evf","level":3,"time":"2025-05-27T17:44:03+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"LDAP Group Member Assoc: memberuid","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
{"reqId":"9mss2xdtNL7nglX37evf","level":3,"time":"2025-05-27T17:44:03+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"addToGroup: ldap_mod_add(cn=group1,dc=files,dc=tilde,dc=xyz,Array\n(\n)\n)","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
{"reqId":"9mss2xdtNL7nglX37evf","level":1,"time":"2025-05-27T17:44:03+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"Add user: User1 to group: Group1","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
Revealing:
getLDAPGroupMemberAssoc()= "memberuid"$entry = []
So it is successfully submitting an empty modification, which is why it's failing silently. So why is that? Why isn't
https://github.com/nextcloud/ldap_write_support/blob/957cfb80c0ad4526f3a4e4cc8256fa91dd35fbe4/lib/LDAPGroupManager.php#L120-L122
happening? ... it's because of case-sensitivity :facepalm:
Patch No. 1
So I tried this patch (on top of the logging patch):
--- /tmp/LDAPGroupManager1.php 2025-05-27 16:16:48.575319174 -0400
+++ /var/www/nextcloud/apps/ldap_write_support/lib/LDAPGroupManager.php 2025-05-27 16:15:30.382773478 -0400
@@ -119,17 +119,17 @@
$entry = [];
$this->logger->error("LDAP Group Member Assoc: " . $this->ldapProvider->getLDAPGroupMemberAssoc($gid));
- switch ($this->ldapProvider->getLDAPGroupMemberAssoc($gid)) {
- case 'memberUid':
+ switch (strtolower($this->ldapProvider->getLDAPGroupMemberAssoc($gid))) {
+ case 'memberuid':
$entry['memberuid'] = $uid;
break;
- case 'uniqueMember':
+ case 'uniquemember':
$entry['uniquemember'] = $this->ldapProvider->getUserDN($uid);
break;
case 'member':
$entry['member'] = $this->ldapProvider->getUserDN($uid);
break;
- case 'gidNumber':
+ case 'gidnumber':
throw new Exception('Cannot add to group when gidNumber is used as relation');
break;
}
And now I still get a silent failure at the user level:
root@files:/var/www/nextcloud# sudo -u www-data php occ group:adduser Group1 User1 ; echo $?
0
but at least I get an error in the logs.
nextcloud.log
{"reqId":"c4MpuSBXzAyl9co9yRRA","level":3,"time":"2025-05-27T17:48:56+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"LDAP Group Member Assoc: memberuid","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
{"reqId":"c4MpuSBXzAyl9co9yRRA","level":3,"time":"2025-05-27T17:48:56+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"addToGroup: ldap_mod_add(cn=group1,dc=files,dc=tilde,dc=xyz,Array\n(\n [memberuid] => User1\n)\n)","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
{"reqId":"c4MpuSBXzAyl9co9yRRA","level":3,"time":"2025-05-27T17:48:56+00:00","remoteAddr":"","user":"--","app":"PHP","method":"","url":"--","message":"ldap_mod_add(): Modify: Object class violation at /var/www/nextcloud/apps/ldap_write_support/lib/LDAPGroupManager.php#138","userAgent":"--","version":"31.0.5.1","data":{"app":"PHP"}}
{"reqId":"c4MpuSBXzAyl9co9yRRA","level":3,"time":"2025-05-27T17:48:56+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"Unable to add user User1 to group Group1","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
I don't know what "Modify: Object class violation" means, so I searched around and tried to replicate the operation. I made an LDAP file and tried to load it:
root@files:~# cat add_user1_to_group1.ldif
dn: cn=group1,dc=files,dc=tilde,dc=xyz
changetype: modify
add: memberuid
memberuid: user1
root@files:~# ldapadd -x -D "cn=admin,dc=files,dc=tilde,dc=xyz" -W -f add_user1_to_group1.ldif
Enter LDAP Password:
modifying entry "cn=group1,dc=files,dc=tilde,dc=xyz"
ldap_modify: Object class violation (65)
additional info: attribute 'memberUid' not allowed
I still had no idea what this meant, but I googled some more until I found out that it's a schema issue. memberUid is defined for objectClass: posixGroup, but looking back, the plugin made groups of objectClass: groupOfNames. And the schema for that wants to use member instead.
Configuration No. 2
I remembered while going in circles and circles and circles on this last night that I'd seen member somewhere in the LDAP settings, and indeed it's Settings -> LDAP/AD Integration -> Advanced -> Directory settings -> Group-Member association
switching it to 'Member (AD)' 🎉 makes everything work suddenly 🎉 ! I'm not running ActiveDirectory (AD), I'm running slapd, but that doesn't matter I guess.
root@files:/var/www/nextcloud# sudo -u www-data php occ group:adduser Group1 User1
root@files:/var/www/nextcloud# sudo -u www-data php occ group:list -i
- Group1:
- displayName: Group1
- backends:
- LDAP
- users:
- User1
root@files:/var/www/nextcloud# sudo -u www-data php occ user:info User1
- user_id: User1
- display_name: User1
- email:
- cloud_id: [email protected]
- enabled: true
- groups:
- Group1
- quota: 10 MB
- storage:
- first_seen: never
- last_seen: never
- user_directory: /var/www/nextcloud/data/User1
- backend: LDAP
nextcloud.log
{"reqId":"4NVJmdQeZl28TYvrfCJB","level":3,"time":"2025-05-27T18:26:43+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"LDAP Group Member Assoc: member","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
{"reqId":"4NVJmdQeZl28TYvrfCJB","level":3,"time":"2025-05-27T18:26:43+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"addToGroup: ldap_mod_add(cn=group1,dc=files,dc=tilde,dc=xyz,Array\n(\n [member] => uid=user1,dc=files,dc=tilde,dc=xyz\n)\n)","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
{"reqId":"4NVJmdQeZl28TYvrfCJB","level":1,"time":"2025-05-27T18:26:43+00:00","remoteAddr":"","user":"--","app":"ldap_write_support","method":"","url":"--","message":"Add user: User1 to group: Group1","userAgent":"--","version":"31.0.5.1","data":{"app":"ldap_write_support"}}
The only use-case for setting Assoc="memberUid" at the moment is if you create your groups using other tools. It's not that crazy, I guess, how often do you really redefine groups? But I don't know what those tools are. Besides NextCloud there's the ActiveDirectory UI or struggling through ldapadd and LDIF files, and that struggle is real. I'm in a small org, so I'm trying to use NextCloud (via this app!) as the main user management UI so that I don't need to teach too many apps to people. If we need to manage an ActiveDirectory domain for 19 people I will cry.
Improvements
-
addToGroup():https://github.com/nextcloud/ldap_write_support/blob/957cfb80c0ad4526f3a4e4cc8256fa91dd35fbe4/lib/LDAPGroupManager.php#L114-L142
-
should interpret the association setting case-insensitively. If this used to work before and now doesn't with NextCloud 31 I suspect this is the primary reason.
-
as the comment mentions being forced to reimplement this because
user_ldapmakes too many, socreateGroupshould move into where it can be better tested. -
could benefit from a
default:to catch undefined settings -
could benefit from reporting the "additional info:" that
ldapaddreturned.That again seems to be an issue for
user_ldap(starting here?). To be followed up I guess. -
errors should throw an exception so that they bubble out to users
-
-
createGroup()should make sure the schema forobjectClassrespects the "Group-Member association".https://github.com/nextcloud/ldap_write_support/blob/957cfb80c0ad4526f3a4e4cc8256fa91dd35fbe4/lib/LDAPGroupManager.php#L61-L81
It always creates a
groupOfNamesregardless of whether the setting is "memberUid" (forposixGroup), "member" (forgroupOfNames) or "uniquemember" (for???). Maybe it should create a group that is BOTHgroupOfNamesandposixGroup?Or maybe in
user_ldap"Group-Member association" should be replaced with a higher-level "Object class". -
Could the UI document that "Group member association" has to match the schema in use?
I can see why this is a tough cookie and why the code ended up weird. It also probably doesn't help that this is a separate project from user_ldap. But a plea to focus on the 80% case (which I'd say are ActiveDirectory and an empty slapd install on Ubuntu) even if it's at the expense of more unusual set ups.
Oh also I don't have memberOf working. I don't really know what it is or how to turn it on. I don't know if that's relevant except that user_ldap wants to use it in restricting login groups. I don't think it's relevant unless it turns out ldap_write_support assumes memberOf is enabled and that messes with stuff one way or the other.
Hello all,
Same problem here! The code $this->ldapProvider->getLDAPGroupMemberAssoc($gid) returns uniquemember in lower case, instead of 'uniqueMember'.
When I add the case 'uniquemember' to the switch statement, then addToGroup and removeFromGroup works
Moreover, for me it is impossible to set Group-Member association manually. In my case this field is blanked out. Whenever I change it, it is again blanked out when returning to this menu. But any way, uniqueMember is the right setting for my setup.
I just see this quote in apps/user_ldap/lib/Group_LDAP.php:
/**
* @var string $ldapGroupMemberAssocAttr contains the LDAP setting (in lower case) with the same name
*/
protected string $ldapGroupMemberAssocAttr;
and here it is (or should be) mixed case. No idea whats right now....
apps/user_ldap/lib/LDAPProvider.php
/**
* Get the LDAP type of association between users and groups
* @param string $gid group id
* @return string the configuration, one of: 'memberUid', 'uniqueMember', 'member', 'gidNumber', ''
* @throws \Exception if group id was not found in LDAP
*/
public function getLDAPGroupMemberAssoc($gid) {
if (!$this->groupBackend->groupExists($gid)) {
throw new \Exception('Group id not found in LDAP');
}
return $this->groupBackend->getLDAPAccess($gid)->getConnection()->getConfiguration()['ldap_group_member_assoc_attribute'];
}
Hi everyone, using your advice I modified the file LDAPGroupManager.php (I'm sharing it with you) and now my nextcloud can create, modify and delete both groups and users in openldap.
ldap_write_support User Template :
dn: uid={UID},{BASE} objectClass: inetOrgPerson objectClass: posixAccount objectClass: top uid: {UID} cn: {UID} sn: {UID} displayName: {UID} givenName: {UID} homeDirectory: {UID} loginShell: {UID} uidNumber: 0 gidNumber: 0 userPassword: {PWD}
LDAPGroupManager.php: `<?php
/**
- SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-FileCopyrightText: 2017-2019 Cooperativa EITA <eita.org.br>
- SPDX-License-Identifier: AGPL-3.0-or-later */
namespace OCA\LdapWriteSupport;
use Exception; use OCA\LdapWriteSupport\AppInfo\Application; use OCA\User_LDAP\Group_Proxy; use OCA\User_LDAP\ILDAPGroupPlugin; use OCP\GroupInterface; use OCP\IGroupManager; use OCP\LDAP\ILDAPProvider; use Psr\Log\LoggerInterface;
class LDAPGroupManager implements ILDAPGroupPlugin { /** @var ILDAPProvider */ private $ldapProvider;
/** @var IGroupManager */
private $groupManager;
public function __construct(
IGroupManager $groupManager,
private LDAPConnect $ldapConnect,
private LoggerInterface $logger,
ILDAPProvider $LDAPProvider,
) {
$this->groupManager = $groupManager;
$this->ldapProvider = $LDAPProvider;
if ($this->ldapConnect->groupsEnabled()) {
$this->makeLdapBackendFirst();
}
}
/**
* Returns the supported actions as int to be
* compared with OC_GROUP_BACKEND_CREATE_GROUP etc.
*
* @return int bitwise-or'ed actions
*/
public function respondToActions() {
if (!$this->ldapConnect->groupsEnabled()) {
return 0;
}
return GroupInterface::CREATE_GROUP |
GroupInterface::DELETE_GROUP |
GroupInterface::ADD_TO_GROUP |
GroupInterface::REMOVE_FROM_GROUP;
}
/**
* @param string $gid
* @return string|null
*/
public function createGroup($gid) {
/**
* FIXME could not create group using LDAPProvider, because its methods rely
* on passing an already inserted [ug]id, which we do not have at this point.
*/
$newGroupEntry = $this->buildNewEntry($gid);
$connection = $this->ldapConnect->getLDAPConnection();
$newGroupDN = "cn=$gid," . $this->ldapConnect->getLDAPBaseGroups()[0];
$newGroupDN = $this->ldapProvider->sanitizeDN([$newGroupDN])[0];
if ($connection && ($ret = ldap_add($connection, $newGroupDN, $newGroupEntry))) {
$message = "Create LDAP group '$gid' ($newGroupDN)";
$this->logger->notice($message, ['app' => Application::APP_ID]);
return $newGroupDN;
} else {
$message = "Unable to create LDAP group '$gid' ($newGroupDN)";
$this->logger->error($message, ['app' => Application::APP_ID]);
return null;
}
}
/**
* delete a group
*
* @param string $gid gid of the group to delete
* @return bool
* @throws Exception
*/
public function deleteGroup($gid) {
$connection = $this->ldapProvider->getGroupLDAPConnection($gid);
$groupDN = $this->ldapProvider->getGroupDN($gid);
if (!$ret = ldap_delete($connection, $groupDN)) {
$message = 'Unable to delete LDAP Group: ' . $gid;
$this->logger->error($message, ['app' => Application::APP_ID]);
} else {
$message = 'Delete LDAP Group: ' . $gid;
$this->logger->notice($message, ['app' => Application::APP_ID]);
}
return $ret;
}
/**
* Add a LDAP user to a LDAP group
*
* @param string $uid Name of the user to add to group
* @param string $gid Name of the group in which add the user
* @return bool
*
* Adds a LDAP user to a LDAP group.
* @throws Exception
*/
public function addToGroup($uid, $gid) {
$connection = $this->ldapProvider->getGroupLDAPConnection($gid);
$groupDN = $this->ldapProvider->getGroupDN($gid);
$entry = [];
switch ($this->ldapProvider->getLDAPGroupMemberAssoc($gid)) {
case 'memberUid':
$entry['memberuid'] = $uid;
break;
case 'uniquemember':
$entry['uniquemember'] = $this->ldapProvider->getUserDN($uid);
break;
case 'member':
$entry['member'] = $this->ldapProvider->getUserDN($uid);
break;
case 'gidNumber':
throw new Exception('Cannot add to group when gidNumber is used as relation');
break;
}
if (!$ret = ldap_mod_add($connection, $groupDN, $entry)) {
$message = 'Unable to add user ' . $uid . ' to group ' . $gid;
$this->logger->error($message, ['app' => Application::APP_ID]);
} else {
$message = 'Add user: ' . $uid . ' to group: ' . $gid;
$this->logger->notice($message, ['app' => Application::APP_ID]);
}
return $ret;
}
/**
* Removes a LDAP user from a LDAP group
*
* @param string $uid Name of the user to remove from group
* @param string $gid Name of the group from which remove the user
* @return bool
*
* removes the user from a group.
* @throws Exception
*/
public function removeFromGroup($uid, $gid) {
$connection = $this->ldapProvider->getGroupLDAPConnection($gid);
$groupDN = $this->ldapProvider->getGroupDN($gid);
$entry = [];
switch ($this->ldapProvider->getLDAPGroupMemberAssoc($gid)) {
case 'memberUid':
$entry['memberuid'] = $uid;
break;
case 'uniquemember':
$entry['uniquemember'] = $this->ldapProvider->getUserDN($uid);
break;
case 'member':
$entry['member'] = $this->ldapProvider->getUserDN($uid);
break;
case 'gidNumber':
throw new Exception('Cannot remove from group when gidNumber is used as relation');
}
if (!$ret = ldap_mod_del($connection, $groupDN, $entry)) {
$message = 'Unable to remove user: ' . $uid . ' from group: ' . $gid;
$this->logger->error($message, ['app' => Application::APP_ID]);
} else {
$message = 'Remove user: ' . $uid . ' from group: ' . $gid;
$this->logger->notice($message, ['app' => Application::APP_ID]);
}
return $ret;
}
public function countUsersInGroup($gid, $search = '') {
return false;
}
public function getGroupDetails($gid) {
return false;
}
public function isLDAPGroup($gid): bool {
try {
return !empty($this->ldapProvider->getGroupDN($gid));
} catch (Exception) {
return false;
}
}
private function buildNewEntry($gid): array {
return [
'objectClass' => ['groupOfUniqueNames', 'top'],
'cn' => $gid,
'uniqueMember' => ['']
];
}
public function makeLdapBackendFirst(): void {
$backends = $this->groupManager->getBackends();
$otherBackends = [];
$this->groupManager->clearBackends();
foreach ($backends as $backend) {
if ($backend instanceof Group_Proxy) {
$this->groupManager->addBackend($backend);
} else {
$otherBackends[] = $backend;
}
}
#insert other backends: database, etc
foreach ($otherBackends as $backend) {
$this->groupManager->addBackend($backend);
}
}
} `
Like @Aison0, also for me Moreover, it is impossible to set Group-Member association manually. In my case this field is blanked out. Whenever I change it, it is again blanked out when returning to this menu. But any way, uniqueMember is the right setting for my setup.
Thanks everybody.
Wow that's great news @russohub ! I'd be curious to have your version in a .patch form to make it easier to apply if I ever need it. Would you be interested in submitting your version as a PR?
I set myself up to make PRs by forking this repo to https://github.com/kousu/ldap_write_support when I was debugging (it's gone now because it turned out I didn't need to patch in the end)
sudo -i -u www-data
cd /path/to/nextcloud
cd apps
mv ldap_write_support ldap_write_support_upstream
git clone [email protected]:kousu/ldap_write_support
cd ldap_write_support
git checkout v1.14.0 # optional; only if patching against `main` won't work for some reason
git checkout -b my-group-patch
vi lib/LDAPGroupManager.php # make my edits while testing
git add -A .
git commit
git push
From there you can submit a PR and it'll easy to see exactly what needed to change.
🥇