[Port to dtq-dev] Issue dspace-customers#903: harden LDAP auth (email fallback, groupmap guard, logging) - #1408
Conversation
… logging) Three isolated defects in LDAP login: - setEpersonAttributes now falls back to the login e-mail when LDAP provides none, so an EPerson is never created/updated with a null mail. - assignGroups guards a groupmap entry that has no ':' separator (previously an ArrayIndexOutOfBoundsException) -- it logs and skips the malformed entry. - The distinguished name is logged via LogHelper instead of System.out.println. Only the LDAPAuthentication.java hunks are ported; the customer's LDAP config (authentication-ldap.cfg, Dockerfile) stays on customer/vsb-tuo. The source's verbose per-iteration debug logging is intentionally omitted per reference/coding-standards.md. Port of dataquest-dev/dspace-customers#903 (item 2). Source: customer/vsb-tuo 40e30e6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR ports targeted hardening improvements to LDAPAuthentication to avoid bad/unsafe behavior during LDAP login (null EPerson email on first login, malformed groupmap entries crashing group assignment, and stdout debug output).
Changes:
- Pass the computed login email into
setEpersonAttributes()and add an overload to fall back to that email when LDAP provides nomailattribute. - Guard
groupmapparsing inassignGroups()to skip (and log) malformed entries instead of throwingArrayIndexOutOfBoundsException. - Replace
System.out.println()DN output with structured logging viaLogHelper.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Log the distinguished name at DEBUG (not INFO) in assignGroups, matching the existing "got DN" DEBUG line and keeping a semi-sensitive DN off the default log level. - Reword the setEpersonAttributes e-mail comment: the method leaves the e-mail unchanged when neither LDAP nor the login address supplies one; the non-null guarantee for the create paths lives in the caller, not here. Addresses Copilot review on #1408. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Addressed the Copilot review in 640f46b (non-blocking polish, no behavior change):
Both threads resolved. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java:764
- This new config-parsing behavior (skip malformed groupmap entries and log an error) is unit-testable without an LDAP server. Since this package already has JUnit tests, adding a focused test for a missing/invalid ':' (and blank-side cases, if handled) would help prevent regressions in auth hardening.
if (t.length < 2) {
log.error(LogHelper.getHeader(context, "assignGroups",
"malformed groupmap entry at index " + groupmapIndex + ": " + groupMap +
" - missing ':' separator"));
groupMap = configurationService.getProperty(
The malformed-entry guard in assignGroups only rejected a missing ':'. An entry
with an empty left part (e.g. ":admins") still passed, leaving an empty
ldapSearchString; the subsequent containsIgnoreCase(dn, "" + ",") then matched
essentially every DN and assigned the mapped group to all LDAP users.
Tighten the guard to also reject blank left/right parts (StringUtils.isBlank),
and parse with split(":", 2) so a colon inside a DSpace group name is preserved
instead of truncated. Well-formed entries parse identically; only genuinely
malformed lines (":group", "group:", ":") are now skipped.
Addresses Copilot review on #1408.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java:766
- The new
groupmapparsing guard is a functional behavior change (skipping malformed entries instead of throwing) but there’s no automated test coverage for it. There are existing authentication unit tests in this module (e.g.ShibHeadersTest,IPMatcherTest), so adding a focused unit test for malformedauthentication-ldap.login.groupmap.<n>values (missing ':', leading ':', trailing ':', and whitespace-only parts) would help prevent regressions without requiring an LDAP server.
while (groupMap != null) {
String t[] = groupMap.split(":", 2);
if (t.length < 2 || StringUtils.isBlank(t[0]) || StringUtils.isBlank(t[1])) {
log.error(LogHelper.getHeader(context, "assignGroups",
"malformed groupmap entry at index " + groupmapIndex + ": " + groupMap +
" - expected '<ldapSearchFragment>:<dspaceGroupName>' with both parts non-empty"));
groupMap = configurationService.getProperty(
"authentication-ldap.login.groupmap." + ++groupmapIndex);
continue;
Problem
Three defects in LDAP authentication (
LDAPAuthentication.java):mailattribute creates/updates anEPersonwith a null e-mail, even when the user typed a valid e-mail to log in.groupmapconfig entry missing its:separator throwsArrayIndexOutOfBoundsExceptionand aborts group assignment.System.out.println("dn:" + dn)instead of the logger.Port of dataquest-dev/dspace-customers#903 (item 2). Only the
LDAPAuthentication.javahunks are ported; the customer'sauthentication-ldap.cfg/ Dockerfile changes stay oncustomer/vsb-tuo.Source provenance (verified by diff, not commit message)
The issue's item 2 cites
b64b7859b4,40e30e6fdd,1047f4d787, but only one of those touches this file:customer/vsb-tuo40e30e6fdd.customer/vsb-tuoa097b030b1("Added logs to see more info about ldap error") — not listed in the issue; the two cited siblingsb64b7859b4(authentication.cfg) and1047f4d787(Dockerfile) are config-only and out of scope.Root cause
setEpersonAttributes()set the e-mail only fromldap.ldapEmail; when that was empty there was no fallback to the address the user authenticated with.assignGroups()didString t[] = groupMap.split(":"); … t[1]with no length check.Change set
LDAPAuthentication.javaonly:setEpersonAttributes(context, eperson, ldap, netid, email); whenldap.ldapEmailis empty, fall back to the suppliedemail. The effective fix is in the self-register/create branch (a freshly createdEPersonotherwise persists with a null mail); the already-registered-by-mail branch also passes it (harmless — that record was just looked up by that e-mail). The original 4-arg signature delegates withnull, so other callers are unchanged.assignGroups(), guard thegroupmapentry parse: split on the first:(split(":", 2)) and skip the entry (log an error naming the index,continueto the nextgroupmap.<n>) when it has fewer than 2 fields or either part is blank. This stops the originalArrayIndexOutOfBoundsExceptionand also rejects an empty search fragment such as:group, which would otherwise match every DN and assign the mapped group to all LDAP users.System.out.println("dn:" + dn)withlog.debug(LogHelper.getHeader(context, "assignGroups", "dn=" + dn))— DEBUG (not INFO) to match the existinggot DNline and keep a semi-sensitive DN off the default log level.Not ported: the source's verbose per-iteration
log.infoarray-dump lines (debug scaffolding) — omitted perreference/coding-standards.md(no narration logging). Only the functional guard + a single dn log line are kept. This accounts for the smaller diff vs the customer commits.Review addressed (commit
640f46bd88)INFO→DEBUG(Copilot: DN at INFO is semi-sensitive / verbose).setEpersonAttributese-mail comment to match the code — the method leaves the e-mail unchanged when neither LDAP nor the login address supplies one; the non-null guarantee for the create paths lives in the caller (lines 302–318), not this method.Test evidence
LDAP first-login (e-mail fallback path) is not reproducible in the local stack (no LDAP directory server), so per
docker-setup§7 that part is stated, not faked. The groupmap guard is pure config-string parsing and is unit-testable without LDAP — a focused test for the malformed-entry case is a reasonable follow-up (see open point below).Behaviour delta:
EPerson.mailin the create path;groupmapentry with no:now logs an error and is skipped instead of throwingArrayIndexOutOfBoundsException;LogHelperat DEBUG, not printed to stdout.Risk & rollback
Confined to LDAP login. The e-mail fallback only fills a value that was previously null; the groupmap guard only adds a skip on malformed config; the log line replaces stdout. Revert = drop the two commits on this branch.
Open points (non-blocking)
groupmapcase is the one gap worth closing (tracked as follow-up rather than expanding this PR's scope).dspace-customers#903), so GitHub's development section cannot auto-link it; the reference above is the trace. A status/provenance note is posted on use dspace.url to dspace.server.url #903.Notes / assumptions
LDAP directory config is customer-specific and intentionally left on
customer/vsb-tuo.