Skip to content

Fix group member attribute selection for 389 Directory Server#161

Open
c1-dev-bot[bot] wants to merge 1 commit into
mainfrom
fix/389ds-group-member-attribute
Open

Fix group member attribute selection for 389 Directory Server#161
c1-dev-bot[bot] wants to merge 1 commit into
mainfrom
fix/389ds-group-member-attribute

Conversation

@c1-dev-bot

@c1-dev-bot c1-dev-bot Bot commented May 6, 2026

Copy link
Copy Markdown

Summary

  • Fix LDAP Object Class Violation (code 65) when granting/revoking group membership on 389 Directory Server
  • The Grant and Revoke methods previously defaulted to uniqueMember (used by groupOfUniqueNames) for groups that didn't match posixGroup or ipausergroup/AD patterns
  • 389 DS typically uses groupOfNames which requires the member attribute, not uniqueMember
  • Restructured the object class switch to explicitly check for groupOfUniqueNames (the only class needing uniqueMember) and default to member for all other group types

Details

The root cause was in pkg/connector/group.go in both the Grant() and Revoke() methods. The switch statement logic was:

  1. posixGroupmemberUid (correct)
  2. ipausergroup or has objectGUIDmember (correct but overly specific)
  3. defaultuniqueMember (wrong for groupOfNames, group, etc.)

The fix changes this to:

  1. posixGroupmemberUid (unchanged)
  2. groupOfUniqueNamesuniqueMember (explicitly targeted)
  3. defaultmember (correct for groupOfNames, group, ipausergroup, AD groups, etc.)

This resolves the TODO comment that was already in the Revoke method acknowledging this needed to be addressed.

Test plan

  • Verify grant/revoke works against 389 Directory Server with groupOfNames groups
  • Verify grant/revoke still works against OpenLDAP with groupOfUniqueNames groups
  • Verify grant/revoke still works against Active Directory with group object class
  • Verify grant/revoke still works with posixGroup groups

Fixes: CXH-1451


Automated PR Notice

This PR was automatically created by c1-dev-bot as a potential implementation.

This code requires:

  • Human review of the implementation approach
  • Manual testing to verify correctness
  • Approval from the appropriate team before merging

The Grant and Revoke methods used a heuristic that defaulted to the
uniqueMember attribute (groupOfUniqueNames) for groups that were not
posixGroup or ipausergroup/AD. 389 Directory Server uses groupOfNames,
which requires the member attribute instead, causing an LDAP Object
Class Violation (code 65).

Restructure the switch to explicitly check for groupOfUniqueNames
(the only standard object class that uses uniqueMember) and default
to the member attribute for all other group types (groupOfNames,
group, ipausergroup, AD groups with objectGUID, etc.).

Fixes: CXH-1451
@c1-dev-bot c1-dev-bot Bot requested a review from a team May 6, 2026 20:26
@linear-code

linear-code Bot commented May 6, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Claude finished @c1-dev-bot[bot]'s task in 1m 39s —— View job


PR Review: Fix group member attribute selection for 389 Directory Server

  • Gather context and read changed files
  • Analyze the diff against origin/main
  • Provide code review feedback

Verdict: Approve — this is a clean, correct fix with sound reasoning.

What changed

The Grant() and Revoke() methods in pkg/connector/group.go restructured their switch logic for choosing which LDAP attribute to modify when adding/removing group members:

Object Class Before After
posixGroup memberUid memberUid (unchanged)
ipausergroup / has objectGUID member (removed — handled by default)
groupOfUniqueNames (fell through to default) uniqueMember (explicit case)
default uniqueMember member

Why this is correct

  1. groupOfUniqueNames is the only standard LDAP objectClass that uses uniqueMember. Making it the explicit case and defaulting everything else to member is the right inversion. RFC 4519 (groupOfNames) and AD (group) both use member.

  2. Removing the objectGUID proxy check is fine. The old code used objectGUID presence as a heuristic for Active Directory. AD's group objectClass uses the member attribute, which is now the default — so AD groups are handled correctly without the special case.

  3. ipausergroup removal is safe. FreeIPA's ipausergroup also uses the member attribute, so the default case covers it.

  4. Sync-side consistency is maintained. The Grants() method at line 276 already reads from all three attributes (uniqueMember, member, memberUid), so the read path handles any group type regardless of which attribute stores membership.

  5. Grant and Revoke remain symmetrical — the switch logic is identical in both methods, which is important for correctness.

Minor issue

Const alignment (group.go:48): attrGroupDescription lost its column alignment with the other constants after attrGroupObjectGUID was removed. The original had two spaces before =; the new version has one. Running gofmt should fix this, but it's cosmetic and non-blocking.

// Current (misaligned):
attrGroupMemberPosix  = "memberUid"
attrGroupDescription = "description"  // ← one space short

// Expected:
attrGroupMemberPosix  = "memberUid"
attrGroupDescription  = "description"

Fix this →

Summary

The logic change is correct and well-targeted. The old default of uniqueMember was wrong for the majority of LDAP directory servers (389 DS, AD, FreeIPA), and the fix properly narrows uniqueMember to the one objectClass that actually requires it. The TODO in the Revoke method is appropriately resolved by this change.

@gromande

gromande commented May 6, 2026

Copy link
Copy Markdown

I created another PR before the bot created this one: #160
My PR also includes a fix for LDAP account creation, which was also broken

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant