test(ldap): reproduce GH #980 divergent-principal DAV bug via OpenLDAP#997
Conversation
Adds an `ldap` docker-compose profile + integration lane that provides the live RED→GREEN reproduction of #980 (DAV paths built from the loginName instead of the canonical Nextcloud UID) that the Keycloak lane (#993) could not: login-by-email resolves to the real home (email is a path alias) and user_oidc hardcodes loginName == UID. - `openldap` service (vegardit/openldap:2.6.10) seeds user `alice` from ldap/bootstrap.ldif. Nextcloud's user_ldap maps her to a UID derived from the LDAP entryUUID, so `loginName (alice) != UID` AND `/remote.php/dav/files/alice/` does not resolve to her real home. - user_ldap is configured by an app-hook (app-hooks/post-installation/15-setup-ldap-backend.sh), gated on the openldap service so it is a no-op for every other lane — mirroring 15-setup-keycloak-provider.sh. - tests/server/ldap drives the multi-user BasicAuth MCP service (port 8003) as `alice`; the round-trip is xfail(strict=True): it fails on master (bug present) and xpasses once #980's BaseNextcloudClient._ensure_principal_id discovery lands, at which point the marker should be dropped. - CI matrix gains an `ldap` lane (reuses multi-user-basic + `ldap` profile, no browser); docs added to CLAUDE.md. Verified locally: fresh-install hook auto-configures user_ldap; test xfails on master and xpasses with #980 cherry-picked. Relates to #980. Tracked on Deck card #490. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @cbcoutinho's task in 3m 43s —— View job ReviewThis is a well-constructed reproduction lane — the LDAP-vs-OIDC-vs-email reasoning in the PR description is sound (I traced through 🟢 Nit — stale image reference in the LDIF comment 🟡 Worth a comment — Everything else checks out:
Solid reproduction of #980 and nice writeup of why LDAP was necessary where Keycloak (#993) couldn't do it. Approving from a code-review perspective — the two notes above are optional polish, not blockers. |
… in verify Round-1 review (🔴): the single-user matrix lane runs a broad `integration and not keycloak and not login_flow and not multi_user_basic` filter, which also collected the new `[integration, ldap]` test. That lane does not start mcp-multi-user-basic (port 8003), so the fixture connection fails during SETUP — an ERROR that xfail(strict) does not swallow — breaking the single-user job on every PR. Add `and not ldap` to both clauses, mirroring how keycloak/login_flow/multi_user_basic are already excluded. Also (🟢): the Verify LDAP step hardcoded `s01`; re-derive the config id the same way the app-hook does so the diagnostic stays accurate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Round-1 findings addressed in `9b429204`:
Went with the marker-exclusion fix (option 1) over a fixture skip-guard, for consistency with the sibling keycloak/multi_user_basic backends added to this same file (none of which use a skip guard; only YAML valid; marker collection verified both ways. |
…ew nits) Round-2 🟢 nits (non-blocking): document that the openldap healthcheck searches for the seeded `alice` entry so "healthy" implies the bootstrap LDIF was applied, and note that the `|| true` on the config-id pipeline guards the whole command substitution under `set -e`. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Round-2 nits addressed in |
|
… path Rebased onto master (which now carries the OpenLDAP cbcoutinho#980 reproduction lane from cbcoutinho#997). With this PR's BaseNextcloudClient._ensure_principal_id fix present, the divergent-principal WebDAV round-trip lands in alice's real home, so the test passes — remove the xfail(strict=True) marker and reword the docstring from "reproduction (xfails until the fix)" to a passing regression guard. Verified locally: `pytest -m ldap` → 1 passed with the fix (was 1 xfailed on master without it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>



What
Adds an
ldapdocker-compose profile + integration lane that provides the live RED→GREEN reproduction of #980 (DAV paths built from the loginName instead of the canonical Nextcloud UID). This is the reproduction the Keycloak lane (#993) could not provide.Why LDAP (and not Keycloak/email)
#980's bug needs two conditions together:
loginName != UIDand/remote.php/dav/files/<loginName>/does not resolve to/files/<UID>/. On the CI Nextcloud versions:loginName != UIDbut Nextcloud resolves/files/<email>/to the real home (email is a valid path alias) → no bug.user_oidchardcodesloginName == UID(sha256 hash) inLoginController.php→ no divergence.user_ldapmaps the LDAP loginaliceto a UID derived from the LDAPentryUUID, and the LDAP login is not a files-path alias →/files/alice/genuinely 404s. ✅Verified live:
/files/<uid>/→ 207,/files/alice/→ 404,current-user-principalreturns the UID (so #980's discovery resolves it).How
openldapservice (vegardit/openldap:2.6.10, digest-pinned) seedsalicefromldap/bootstrap.ldif(no data volume → re-seeds fresh eachup).user_ldapis configured by an app-hookapp-hooks/post-installation/15-setup-ldap-backend.sh, gated on theopenldapservice (getent hosts openldap) so it is a no-op for every other lane — mirroring15-setup-keycloak-provider.sh.tests/server/ldap/drives the multi-user BasicAuth MCP service (port 8003) asalice(no browser). The WebDAV round-trip isxfail(strict=True): it fails onmaster(bug present) and xpasses once fix(client): resolve DAV paths via current-user-principal discovery #980'sBaseNextcloudClient._ensure_principal_iddiscovery lands — at which pointstrict=Trueturns the xpass into a CI failure, signalling that the marker should be dropped.ldapmatrix lane (reusesmulti-user-basic+ theldapprofile); docs inCLAUDE.md.Validation (local, NC32)
docker compose --profile ldap --profile multi-user-basic up→ the app-hook auto-configuresuser_ldap(verified in app logs,ldap:test-configvalid).pytest -m ldap→ 1 xfailed (bug present on master).Relationship to #980 / #993
This is the test/infra counterpart to the fix in #980; it's the genuine reproduction that #993 set out to build but couldn't via Keycloak. Drop the
xfailmarker when #980 merges.This PR was generated with the help of AI, and reviewed by a Human