From d7f58e5a33f0df528a0df59c63d86e41ffd2578f Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Sat, 10 Oct 2026 16:36:22 +0200 Subject: [PATCH] fix: push group removals if user gets deleted to SCIM --- backend/internal/service/user_service.go | 18 ++++++++++++ backend/internal/service/user_service_test.go | 28 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index bc0304ab..1daddea1 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -255,11 +255,29 @@ func (s *UserService) DeleteUserInternal(ctx context.Context, cfg *appconfig.App } } + // Remember the user's groups before the delete cascades their memberships away + var groupIDs []string + err = tx. + WithContext(ctx). + Table("user_groups_users"). + Where("user_id = ?", userID). + Pluck("user_group_id", &groupIDs). + Error + if err != nil { + return fmt.Errorf("failed to load user groups of user to delete: %w", err) + } + err = tx.WithContext(ctx).Delete(&user).Error if err != nil { return fmt.Errorf("failed to delete user: %w", err) } + // Bump the UpdatedAt of the groups the user was in, so the SCIM sync pushes the groups without the deleted member + err = s.touchUserGroups(ctx, tx, groupIDs) + if err != nil { + return fmt.Errorf("failed to update user groups timestamp: %w", err) + } + return nil } diff --git a/backend/internal/service/user_service_test.go b/backend/internal/service/user_service_test.go index 87941a98..ae1910ed 100644 --- a/backend/internal/service/user_service_test.go +++ b/backend/internal/service/user_service_test.go @@ -176,3 +176,31 @@ func TestUpdateUserGroupsBumpsRemovedGroupUpdatedAt(t *testing.T) { require.True(t, updated.LastModified().After(past), "group %s changed membership, so its UpdatedAt must be bumped", updated.Name) } } + +func TestDeleteUserBumpsGroupUpdatedAt(t *testing.T) { + config := &appconfig.AppConfigModel{RequireUserEmail: "false"} + userService, groupService := newTestUserService(t) + + group, err := groupService.Create(t.Context(), dto.UserGroupCreateDto{Name: "members", FriendlyName: "Members"}) + require.NoError(t, err) + + user, err := userService.CreateUser(t.Context(), config, dto.UserCreateDto{ + Username: "leaver", + FirstName: "Group", + LastName: "Leaver", + UserGroupIds: []string{group.ID}, + }) + require.NoError(t, err) + + // Backdate the group so a bump is visible regardless of the timestamp precision + past := time.Now().Add(-time.Hour) + require.NoError(t, userService.db.Model(&model.UserGroup{}).Where("id = ?", group.ID).Update("updated_at", datatype.DateTime(past)).Error) + + err = userService.DeleteUser(t.Context(), config, user.ID, false) + require.NoError(t, err) + + updated, err := groupService.Get(t.Context(), group.ID) + require.NoError(t, err) + require.Empty(t, updated.Users) + require.True(t, updated.LastModified().After(past), "deleting a member must bump the group's UpdatedAt") +}