-
Notifications
You must be signed in to change notification settings - Fork 16
LDAP auth doesn't auto create user on some client programs #110
Description
For Apple Calendar, the Baikal automatically creates the User on the MySQL DB as expected using LDAP auth by using the Baikal LDAP patch from YunoHost, but it does not work during the setup of a new Baikal account on MS Outlook through Z-Push, as the way Outlook works (see "Additional information" section). We can patch Z-Push to work in a similar way as Apple Calendar and create the LDAP user automatically, which would fix the issue on Z-Push but not on other misbehavior client apps.
The problem happens because the Baikal LDAP patch from YunoHost doesn't implement the Principal Backend to check users on the LDAP DB directly instead of relying on user existence on the Baikal MySQL DB, so Principal Lookup fails. There are a lot of "IMAP" and "LDAP" patches that implement authentication in a similar way as the YunoHost project does for Baikal, but the only one I could find that implements the Principal Backend is available at sabre-io/Baikal#1124 - it doesn't require auto-creating the user on the MySQL DB, and it always reflects the LDAP directory. Sadly, approaches to implement LDAP/IMAP auth on Baikal are still in review for years now, so it would be faster if we fork the PR and implement it on our own until it's merged to the Baikal repo, similar to our LDAP patch.
Another 'issue' that the LDAP patch from YunoHost has is that it never updates the MySQL DB. If user attributes are renamed on YunoHost, it doesn't reflect on Baikal MySQL DB, which will still show the old data (e.g. Full Name). As the LDAP query has been made anyway, it could retrieve its data and update the Baikal MySQL DB to assure a better experience for inexperienced users, thinking that they are unable to log in to a client app because Baikal "isn't syncing" (showing old data), which doesn't interfere with login at all. It would probably require a cron job to check from time to time for updated or removed users from YunoHost and remove them from the Baikal MySQL DB.
If implementing the Principal Backend to query the LDAP directory is out of question, we could leverage the YunoHost hooks. During Baikal install, we could import all LDAP users into Baikal MySQL DB, and when a user is created, updated or deleted using YunoHost, we could create a hook that also updates his/her information on Baikal MySQL DB.
But I do believe that the definitive solution would be implementing the Principal Backend in our Baikal patch instead of relying on hooks or cron jobs, so it would work even for misbehaving programs.
Additional information:
Gemini replies to me the follows:
🔄 The DAV Request Flow Problem
In a standard WebDAV (SabreDAV) setup, the flow for accessing a resource is:
Step A. Request: Client sends PROPFIND request for a user resource (e.g., address book).
Step B. Authentication Trigger: SabreDAV's authentication plugin triggers the authentication process (calling your custom validateUserPass).
Step C. Authentication Success: validateUserPass is called.
- It calls validateUserPassExternal (LDAP).
- If successful, it calls autoUserCreation (if the user is new).
- It sets $this->currentUser.
Step D. Resource Lookup: SabreDAV attempts to find the resource corresponding to the username (eduardomozart) in its Principal Collection.
1. Apple Calendar (Success) Flow
Robust CalDAV/CardDAV clients like Apple Calendar, before trying to access a specific resource path (like /cal.php/calendars/username/), first send a generic request (often an unauthenticated GET or an authenticated PROPFIND to the root /) to discover the Home Set URL (the path to the user's primary collection).
Step A: Initial Authentication: The client sends credentials, triggering the full Basic Auth flow.
Step B: validateUserPass() Runs: Your LDAPUserBindAuth::validateUserPass() runs successfully.
Step C: Auto-Creation Runs: Since the user doesn't exist locally, autoUserCreation() executes, inserting the user into the users and principals tables.
Step D: Resource Lookup: The client then sends the PROPFIND request for the user's calendars, and the system finds the principal/user in the database. Success.
2. Outlook (Failure) Flow
Outlook's CalDAV implementation (often relying on third-party plugins or built-in, less standard implementations) is known to be more aggressive or non-standard in its approach.
Outlook is likely attempting to access the exact calendar path for the new user before it has fully established the authentication session or before it has properly responded to the 401 Unauthorized challenge with the correct Authorization header on the resource request.
Step A: Request: Outlook sends a request (like PROPFIND /cal.php/calendars/outlookuser/).
Step B: Principal Lookup Fails Immediately: Because the user is new, the SabreDAV Principal Backend checks the database and immediately finds no principal named outlookuser.
Step C: Server Throws Sabre\DAV\Exception\NotFound: The server aborts the request with the "Principal not found" error before the code path can reach validateUserPass() to run autoUserCreation(). Failure.
It's pretty accurate as it's exactly as Z-Push is set up.
During the Z-Push setup, Baikal fails with:
$ sudo cat /var/log/nginx/baikal.nethouse.inf.br-error.log
2025/11/23 01:34:02 [error] 1045#1045: *106 FastCGI sent in stderr: "PHP message: Sabre\DAV\Exception\NotFound: Principal with name eduardomozart not found in /var/www/baikal/vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php:116
Stack trace:
#0 /var/www/baikal/vendor/sabre/dav/lib/DAV/Tree.php(83): Sabre\DAVACL\AbstractPrincipalCollection->getChild()
#1 /var/www/baikal/vendor/sabre/dav/lib/DAV/Server.php(971): Sabre\DAV\Tree->getNodeForPath()
#2 /var/www/baikal/vendor/sabre/dav/lib/DAV/Server.php(1662): Sabre\DAV\Server->getPropertiesIteratorForPath()
#3 /var/www/baikal/vendor/sabre/dav/lib/DAV/Server.php(1647): Sabre\DAV\Server->writeMultiStatus()
#4 /var/www/baikal/vendor/sabre/dav/lib/DAV/CorePlugin.php(346): Sabre\DAV\Server->generateMultiStatus()
#5 /var/www/baikal/vendor/sabre/event/lib/WildcardEmitterTrait.php(89): Sabre\DAV\CorePlugin->httpPropFind()
#6 /var/www/baikal/vendor/sabre/dav/lib/DAV/Server.php(472): Sabre\DAV\Server->emit()
#7 /var/www/baikal/vendor/sabre/dav/lib/DAV/Server.php(253): Sabre\DAV\Server->invokeMethod()
#8 /var/www/baikal/v" while reading response header from upstream, client: ::1, server: baikal.domain.tld, request: "PROPFIND /card.php/addressbooks/eduardomozart/ HTTP/2.0", upstream: "fastcgi://unix:/var/run/php/php8.0-fpm-baikal.sock:", host: "baikal.domain.tld"
$ sudo cat /var/log/z-push/autodiscover-error.log
23/11/2025 01:34:03 [ 1394] [FATAL] [#unknown] Exception: (Exception) - Woops, something's gone wrong! The CardDAV server returned the http status code 404.