You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by la...@apache.org on 2026/10/12 19:21:13 UTC
(airavata-custos) branch master updated: clear identity cache and allocation memberships when deleting user (#622)
This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
The following commit(s) were added to refs/heads/master by this push:
new 92bc9765a clear identity cache and allocation memberships when deleting user (#622)
92bc9765a is described below
commit 92bc9765ac77e0ba9fb65cb84c473bc3777ae1b1
Author: Yasith Jayawardana <ya...@icloud.com>
AuthorDate: Mon Oct 12 15:21:08 2026 -0400
clear identity cache and allocation memberships when deleting user (#622)
* clear identity cache and allocation memberships when deleting user
* reduce it to on delete cascade
---
internal/db/migrations/000004_allocations.up.sql | 3 +-
pkg/service/identity_resolver_integration_test.go | 36 +++++++++++++++++++++++
pkg/service/user.go | 1 +
pkg/service/user_identity.go | 2 ++
pkg/service/user_merge.go | 1 +
pkg/service/user_merge_integration_test.go | 16 ++++++++++
6 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/internal/db/migrations/000004_allocations.up.sql b/internal/db/migrations/000004_allocations.up.sql
index 47a178612..b87e0db84 100644
--- a/internal/db/migrations/000004_allocations.up.sql
+++ b/internal/db/migrations/000004_allocations.up.sql
@@ -149,7 +149,8 @@ CREATE TABLE IF NOT EXISTS compute_allocation_memberships
PRIMARY KEY (id),
CONSTRAINT uq_compute_allocation_memberships_allocation_user UNIQUE (compute_allocation_id, user_id),
CONSTRAINT fk_compute_allocation_memberships_allocation FOREIGN KEY (compute_allocation_id)
- REFERENCES compute_allocations (id) ON DELETE CASCADE
+ REFERENCES compute_allocations (id) ON DELETE CASCADE,
+ CONSTRAINT fk_compute_allocation_memberships_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_compute_allocation_memberships_user ON compute_allocation_memberships (user_id);
CREATE INDEX IF NOT EXISTS idx_compute_allocation_memberships_status ON compute_allocation_memberships (membership_status);
diff --git a/pkg/service/identity_resolver_integration_test.go b/pkg/service/identity_resolver_integration_test.go
index 087ad16f9..2e630d60e 100644
--- a/pkg/service/identity_resolver_integration_test.go
+++ b/pkg/service/identity_resolver_integration_test.go
@@ -295,6 +295,42 @@ func TestResolveCaller_EmailFallback_PublishesIdentityCreated(t *testing.T) {
}
}
+func TestResolveCaller_DropsTheCachedCallerOnIdentityChange(t *testing.T) {
+ cases := map[string]func(svc *Service, user, other, identityID string) error{
+ "identity deleted": func(svc *Service, _, _, id string) error { return svc.DeleteUserIdentity(ctx(), id) },
+ "identity rebound": func(svc *Service, _, _, id string) error {
+ return svc.UpdateUserIdentity(ctx(), &models.UserIdentity{ID: id, OIDCSub: "sub-other"})
+ },
+ "user deleted": func(svc *Service, user, _, _ string) error { return svc.DeleteUser(ctx(), user) },
+ "user merged": func(svc *Service, user, other, _ string) error {
+ _, err := svc.MergeUsers(ctx(), other, user)
+ return err
+ },
+ }
+ for name, change := range cases {
+ t.Run(name, func(t *testing.T) {
+ database := setupTestDB(t)
+ svc := newTestService(database)
+ user := seedUser(t, database, "cached@example.edu")
+ seedOIDCIdentity(t, database, user, "sub-cached", "cached@example.edu")
+ var identityID string
+ if err := database.Get(&identityID, "SELECT id FROM user_identities WHERE user_id = $1", user); err != nil {
+ t.Fatalf("read identity: %v", err)
+ }
+ claims := &identity.Claims{Sub: "sub-cached", Email: "cached@example.edu", EmailVerified: true}
+ if _, _, err := svc.ResolveCaller(ctx(), claims); err != nil {
+ t.Fatalf("resolve: %v", err)
+ }
+ if err := change(svc, user, seedUser(t, database, "survivor@example.edu"), identityID); err != nil {
+ t.Fatalf("change: %v", err)
+ }
+ if caller, _, err := svc.ResolveCaller(ctx(), claims); err == nil && caller.UserID == user {
+ t.Fatal("cached caller survived the change")
+ }
+ })
+ }
+}
+
func TestResolveCaller_RefusesDisabledUser(t *testing.T) {
database := setupTestDB(t)
svc := newTestService(database)
diff --git a/pkg/service/user.go b/pkg/service/user.go
index 8e76fb40c..b9e6b88a9 100644
--- a/pkg/service/user.go
+++ b/pkg/service/user.go
@@ -245,5 +245,6 @@ func (s *Service) DeleteUser(ctx context.Context, id string) error {
}); err != nil {
return fmt.Errorf("delete user: %w", err)
}
+ s.InvalidateAllIdentities()
return nil
}
diff --git a/pkg/service/user_identity.go b/pkg/service/user_identity.go
index dbffc363f..a5e01e7d7 100644
--- a/pkg/service/user_identity.go
+++ b/pkg/service/user_identity.go
@@ -166,6 +166,7 @@ func (s *Service) UpdateUserIdentity(ctx context.Context, e *models.UserIdentity
}); err != nil {
return fmt.Errorf("update user identity: %w", err)
}
+ s.InvalidateAllIdentities()
return nil
}
@@ -189,5 +190,6 @@ func (s *Service) DeleteUserIdentity(ctx context.Context, id string) error {
}); err != nil {
return fmt.Errorf("delete user identity: %w", err)
}
+ s.InvalidateAllIdentities()
return nil
}
diff --git a/pkg/service/user_merge.go b/pkg/service/user_merge.go
index 913acbe70..faea905c7 100644
--- a/pkg/service/user_merge.go
+++ b/pkg/service/user_merge.go
@@ -131,6 +131,7 @@ func (s *Service) MergeUsers(ctx context.Context, survivingID, retiringID string
}); err != nil {
return nil, fmt.Errorf("merge users: %w", err)
}
+ s.InvalidateAllIdentities()
return survivor, nil
}
diff --git a/pkg/service/user_merge_integration_test.go b/pkg/service/user_merge_integration_test.go
index 92c6200e4..a5438f583 100644
--- a/pkg/service/user_merge_integration_test.go
+++ b/pkg/service/user_merge_integration_test.go
@@ -135,3 +135,19 @@ func TestMergeUsers_KeepsHigherTagAndPIHoldsNoTag(t *testing.T) {
}
}
}
+
+func TestDeleteUser_RemovesTheirAllocationMemberships(t *testing.T) {
+ database := setupTestDB(t)
+ svc := newTestService(database)
+ _, allocID := newClusterAllocation(t, svc, seedUser(t, database, "pi@example.edu"))
+ member := seedUser(t, database, "member@example.edu")
+ if _, err := svc.CreateComputeAllocationMembership(ctx(), &models.ComputeAllocationMembership{ComputeAllocationID: allocID, UserID: member, MembershipStatus: models.ACTIVE}); err != nil {
+ t.Fatalf("seed membership: %v", err)
+ }
+ if err := svc.DeleteUser(ctx(), member); err != nil {
+ t.Fatalf("delete user: %v", err)
+ }
+ if left, _ := svc.memberships.FindByUser(ctx(), member); len(left) != 0 {
+ t.Fatalf("%d memberships left", len(left))
+ }
+}