Skip to content

geonode_ldap remove_user_memberships() deletes Group objects instead of removing user membership #239

@t-book

Description

@t-book

Ldap: remove_user_memberships() deletes Group objects instead of removing user membership

Description

The remove_user_memberships() function in backend.py deletes Django Group objects instead of removing the user from those groups. This causes associated GroupProfiles to be cascade-deleted, resulting in data loss.

Affected Code

geonode_ldap/backend.py, line 278:

def remove_user_memberships(user):
    existing_group_profile_memberships = GroupMember.objects.filter(
        user=user).values_list("group__slug", flat=True)
    user.groups.filter(name__in=existing_group_profile_memberships).delete()  # BUG: deletes Group objects!
    GroupMember.objects.filter(user=user).delete()

Problem

user.groups.filter(...).delete() deletes the Group objects themselves, not the user's membership in those groups.

Since GroupProfile.group has on_delete=CASCADE, this causes the associated GroupProfiles to be deleted at the database level.

Impact

  • When AUTH_LDAP_MIRROR_GROUPS=True, every LDAP user login can delete groups
  • Groups that other users belong to are destroyed
  • No Django admin log is created (cascade delete bypasses ORM logging)
  • Difficult to diagnose - groups silently disappear

Steps to Reproduce

  1. Enable AUTH_LDAP_MIRROR_GROUPS = True
  2. Create a GroupProfile with members
  3. Have an LDAP user who is a member of that group log in
  4. The Django Group and GroupProfile are deleted

Suggested Fix

def remove_user_memberships(user):
    """Remove user from all GroupMember instances and Django groups.
    
    This function cleans up a user's group memberships before reassigning
    new ones based on LDAP. It operates on both:
    - Django's auth.Group (used for permissions)
    - GeoNode's GroupMember (used for UI and group management)
    """
    
    # Get list of GroupProfile slugs where this user is currently a member
    # GroupMember links users to GroupProfiles, and we need the slug
    # because Django Group.name matches GroupProfile.slug
    existing_group_profile_memberships = GroupMember.objects.filter(
        user=user).values_list("group__slug", flat=True)
    
    # Find the Django Group objects that match these slugs
    # user.groups is the ManyToMany relation to auth.Group
    groups_to_remove = user.groups.filter(name__in=existing_group_profile_memberships)
    
    # CORRECT: Use .remove() to remove the user from these groups
    # This only removes the relationship (user <-> group), NOT the Group objects
    # The * unpacks the queryset into individual arguments
    user.groups.remove(*groups_to_remove)
    
    # Delete the GeoNode GroupMember entries for this user
    # This removes the user's membership records from GeoNode's group system
    GroupMember.objects.filter(user=user).delete()

Environment

  • GeoNode 4.x
  • geonode_ldap from geonode-contribs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions