From cac3b2986ad3501c99c402a5a987459fba1d4ec7 Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Wed, 17 Jun 2026 11:58:29 -0700 Subject: [PATCH] Grant users access to their own sync topic under deny-all Manager.Authorize had no special case for a user's own sync topic, so with auth-default-access=deny-all the per-account sync topic (st_...) fell through to the default access and was denied. This broke web app account sync for non-admin users: wss://.../st_/ws returned 403. Admin-role users were unaffected via the existing role bypass. Allow a user full access to their own SyncTopic in Authorize. This fixes existing users without a migration, since it needs no ACL row. Fixes #733 Co-Authored-By: Claude Opus 4.8 (1M context) --- user/manager.go | 7 +++++++ user/manager_test.go | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/user/manager.go b/user/manager.go index 34d96403..79fbe26b 100644 --- a/user/manager.go +++ b/user/manager.go @@ -639,6 +639,13 @@ func (a *Manager) Authorize(user *User, topic string, perm Permission) error { if user != nil && user.Role == RoleAdmin { return nil // Admin can do everything } + // A user always has full access to their own sync topic, which the apps use + // to sync subscriptions/settings across devices. Without this, an + // auth-default-access of "deny-all" locks the user out of their own sync + // topic (no ACL entry is created for it at user creation). See #733. + if user != nil && user.SyncTopic != "" && topic == user.SyncTopic { + return nil + } username := Everyone if user != nil { username = user.Name diff --git a/user/manager_test.go b/user/manager_test.go index 6d13929e..6e5a6a05 100644 --- a/user/manager_test.go +++ b/user/manager_test.go @@ -129,6 +129,13 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) { require.Nil(t, a.Authorize(ben, "announcements", PermissionRead)) require.Equal(t, ErrUnauthorized, a.Authorize(ben, "announcements", PermissionWrite)) + // User has full access to their own sync topic, even under deny-all, + // but not to another user's sync topic (#733) + require.Nil(t, a.Authorize(ben, ben.SyncTopic, PermissionRead)) + require.Nil(t, a.Authorize(ben, ben.SyncTopic, PermissionWrite)) + require.Equal(t, ErrUnauthorized, a.Authorize(ben, john.SyncTopic, PermissionRead)) + require.Equal(t, ErrUnauthorized, a.Authorize(ben, john.SyncTopic, PermissionWrite)) + // User john should have // "deny" to mytopic_deny*, // "ro" to mytopic_ro*,