From 08f2190ad1e732e12d5e31ff8b59a116b4ef2af1 Mon Sep 17 00:00:00 2001 From: Gani Georgiev Date: Tue, 19 Nov 2024 22:36:32 +0200 Subject: [PATCH] [#5898] instead of unregister, unset the realtime client auth state on delete of the related auth record --- CHANGELOG_16_22.md | 5 +++ apis/realtime.go | 8 ++--- apis/realtime_test.go | 78 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/CHANGELOG_16_22.md b/CHANGELOG_16_22.md index 7522e85d..74b11f74 100644 --- a/CHANGELOG_16_22.md +++ b/CHANGELOG_16_22.md @@ -2,6 +2,11 @@ > For the most recent versions, please refer to [CHANGELOG.md](./CHANGELOG.md) --- +## v0.22.27 + +- Instead of unregistering the realtime clients, we now just unset their auth state on delete of the related auth record so that the clients can receive the `delete` event ([#5898](https://github.com/pocketbase/pocketbase/issues/5898)). + + ## v0.22.26 - (_Backported from v0.23.0-rc_) Added manual WAL checkpoints before creating the zip backup to minimize copying unnecessary data. diff --git a/apis/realtime.go b/apis/realtime.go index c0e76e4d..4f2402a0 100644 --- a/apis/realtime.go +++ b/apis/realtime.go @@ -242,8 +242,8 @@ func realtimeUpdateClientsAuth(app core.App, newAuthRecord *core.Record) error { return group.Wait() } -// unregisterClientsByAuthModel unregister all clients that has the provided auth model. -func realtimeUnregisterClientsByAuth(app core.App, authModel core.Model) error { +// realtimeUnsetClientsAuthState unsets the auth state of all clients that have the provided auth model. +func realtimeUnsetClientsAuthState(app core.App, authModel core.Model) error { chunks := app.SubscriptionsBroker().ChunkedClients(clientsChunkSize) group := new(errgroup.Group) @@ -255,7 +255,7 @@ func realtimeUnregisterClientsByAuth(app core.App, authModel core.Model) error { if clientAuth != nil && clientAuth.Id == authModel.PK() && clientAuth.Collection().Name == authModel.TableName() { - app.SubscriptionsBroker().Unregister(client.Id()) + client.Unset(RealtimeClientAuthKey) } } @@ -293,7 +293,7 @@ func bindRealtimeEvents(app core.App) { Func: func(e *core.ModelEvent) error { collection := realtimeResolveRecordCollection(e.App, e.Model) if collection != nil && collection.IsAuth() { - if err := realtimeUnregisterClientsByAuth(e.App, e.Model); err != nil { + if err := realtimeUnsetClientsAuthState(e.App, e.Model); err != nil { app.Logger().Warn( "Failed to remove client(s) associated to the deleted auth model", slog.Any("id", e.Model.PK()), diff --git a/apis/realtime_test.go b/apis/realtime_test.go index bfdd1e38..c9df88c0 100644 --- a/apis/realtime_test.go +++ b/apis/realtime_test.go @@ -427,26 +427,51 @@ func TestRealtimeAuthRecordDeleteEvent(t *testing.T) { // init realtime handlers apis.NewRouter(testApp) - authRecord, err := testApp.FindAuthRecordByEmail("users", "test@example.com") + authRecord1, err := testApp.FindFirstRecordByData("users", "email", "test@example.com") if err != nil { t.Fatal(err) } - client := subscriptions.NewDefaultClient() - client.Set(apis.RealtimeClientAuthKey, authRecord) - testApp.SubscriptionsBroker().Register(client) + authRecord2, err := testApp.FindFirstRecordByData("users", "email", "test2@example.com") + if err != nil { + t.Fatal(err) + } + + client1 := subscriptions.NewDefaultClient() + client1.Set(apis.RealtimeClientAuthKey, authRecord1) + testApp.SubscriptionsBroker().Register(client1) + + client2 := subscriptions.NewDefaultClient() + client2.Set(apis.RealtimeClientAuthKey, authRecord1) + testApp.SubscriptionsBroker().Register(client2) + + client3 := subscriptions.NewDefaultClient() + client3.Set(apis.RealtimeClientAuthKey, authRecord2) + testApp.SubscriptionsBroker().Register(client3) // mock delete event e := new(core.ModelEvent) e.App = testApp e.Type = core.ModelEventTypeDelete e.Context = context.Background() - e.Model = authRecord + e.Model = authRecord1 testApp.OnModelAfterDeleteSuccess().Trigger(e) - if total := len(testApp.SubscriptionsBroker().Clients()); total != 0 { - t.Fatalf("Expected no subscription clients, found %d", total) + if total := len(testApp.SubscriptionsBroker().Clients()); total != 3 { + t.Fatalf("Expected %d subscription clients, found %d", 3, total) + } + + if auth := client1.Get(apis.RealtimeClientAuthKey); auth != nil { + t.Fatalf("[client1] Expected the auth state to be unset, found %#v", auth) + } + + if auth := client2.Get(apis.RealtimeClientAuthKey); auth != nil { + t.Fatalf("[client2] Expected the auth state to be unset, found %#v", auth) + } + + if auth := client3.Get(apis.RealtimeClientAuthKey); auth == nil || auth.(*core.Record).Id != authRecord2.Id { + t.Fatalf("[client3] Expected the auth state to be left unchanged, found %#v", auth) } } @@ -524,17 +549,30 @@ func TestRealtimeCustomAuthModelDeleteEvent(t *testing.T) { // init realtime handlers apis.NewRouter(testApp) - authRecord, err := testApp.FindAuthRecordByEmail("users", "test@example.com") + authRecord1, err := testApp.FindFirstRecordByData("users", "email", "test@example.com") if err != nil { t.Fatal(err) } - client := subscriptions.NewDefaultClient() - client.Set(apis.RealtimeClientAuthKey, authRecord) - testApp.SubscriptionsBroker().Register(client) + authRecord2, err := testApp.FindFirstRecordByData("users", "email", "test2@example.com") + if err != nil { + t.Fatal(err) + } + + client1 := subscriptions.NewDefaultClient() + client1.Set(apis.RealtimeClientAuthKey, authRecord1) + testApp.SubscriptionsBroker().Register(client1) + + client2 := subscriptions.NewDefaultClient() + client2.Set(apis.RealtimeClientAuthKey, authRecord1) + testApp.SubscriptionsBroker().Register(client2) + + client3 := subscriptions.NewDefaultClient() + client3.Set(apis.RealtimeClientAuthKey, authRecord2) + testApp.SubscriptionsBroker().Register(client3) // refetch the authRecord as CustomUser - customUser, err := findCustomUserByEmail(testApp, "test@example.com") + customUser, err := findCustomUserByEmail(testApp, authRecord1.Email()) if err != nil { t.Fatal(err) } @@ -544,8 +582,20 @@ func TestRealtimeCustomAuthModelDeleteEvent(t *testing.T) { t.Fatal(err) } - if total := len(testApp.SubscriptionsBroker().Clients()); total != 0 { - t.Fatalf("Expected no subscription clients, found %d", total) + if total := len(testApp.SubscriptionsBroker().Clients()); total != 3 { + t.Fatalf("Expected %d subscription clients, found %d", 3, total) + } + + if auth := client1.Get(apis.RealtimeClientAuthKey); auth != nil { + t.Fatalf("[client1] Expected the auth state to be unset, found %#v", auth) + } + + if auth := client2.Get(apis.RealtimeClientAuthKey); auth != nil { + t.Fatalf("[client2] Expected the auth state to be unset, found %#v", auth) + } + + if auth := client3.Get(apis.RealtimeClientAuthKey); auth == nil || auth.(*core.Record).Id != authRecord2.Id { + t.Fatalf("[client3] Expected the auth state to be left unchanged, found %#v", auth) } }