From 8b81295458733a5042f0431742d45473d84b9542 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Mon, 26 Oct 2026 19:33:52 +0100 Subject: [PATCH] fix: revoke chat access as soon as a user leaves the chat group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BM chat context implements golem15.chat's LiveMembership, so every participant check also requires chat-group membership: a removed user loses # ogólny, their DMs, uploads, subscriptions and chat:message delivery at once instead of after the next participant sync. --- classes/chat_context.go | 7 +++++++ classes/chat_context_test.go | 3 +++ 2 files changed, 10 insertions(+) diff --git a/classes/chat_context.go b/classes/chat_context.go index 3755c2f..4053106 100644 --- a/classes/chat_context.go +++ b/classes/chat_context.go @@ -12,6 +12,7 @@ import ( var ( _ chatcontracts.Context = (*ChatContext)(nil) _ chatcontracts.MessageModerator = (*ChatContext)(nil) + _ chatcontracts.LiveMembership = (*ChatContext)(nil) _ chancontracts.AccessResolver = (*ChatAccess)(nil) ) @@ -134,6 +135,12 @@ func (c *ChatContext) CanModerateMessage(ctx context.Context, userID uint, conve return IsChatModerator(ctx, c.db, userID, c.moderatorGroup) } +// RequireLiveMembership is always true: golem15.chat checks HasMember on +// every participant check of the BM chat, so someone taken out of the +// chat group loses # ogólny and their DMs at once, not when their +// participant rows are pruned. +func (c *ChatContext) RequireLiveMembership() bool { return true } + // HasMember reports whether userID is in the chat group. func (c *ChatContext) HasMember(ctx context.Context, userID uint) (bool, error) { if userID == 0 || c.group == "" { diff --git a/classes/chat_context_test.go b/classes/chat_context_test.go index 49f644a..5483196 100644 --- a/classes/chat_context_test.go +++ b/classes/chat_context_test.go @@ -92,6 +92,9 @@ func TestChatContext(t *testing.T) { if _, err := c.HasMember(ctx, 13); err == nil { t.Error("HasMember with a failing check returned no error") } + if !c.RequireLiveMembership() { + t.Error("RequireLiveMembership = false, want true: removal from the chat group must revoke access at once") + } if !c.CanActFor(ctx, 7, 7) || c.CanActFor(ctx, 7, 8) { t.Error("CanActFor is not self-only") }