From 03d405ed807f37e3cac836cb62b5ff394ac051d3 Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Sun, 31 May 2026 11:42:54 -0400 Subject: [PATCH] Rename acl cahce --- user/access_cache.go | 16 ++++++++-------- user/access_cache_test.go | 18 +++++++++--------- user/manager.go | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/user/access_cache.go b/user/access_cache.go index a49327d2..cfd983b3 100644 --- a/user/access_cache.go +++ b/user/access_cache.go @@ -8,7 +8,7 @@ import ( "heckel.io/ntfy/v2/db" ) -// aclCache is an in-memory index over the entire user_access table. +// accessCache is an in-memory index over the entire user_access table. // // exact[username][escapedTopic] returns the matching entry in O(1) for the common // case where the requested topic appears verbatim in some rule. The key is the @@ -18,7 +18,7 @@ import ( // pattern[username] is the linear-scan list of %-bearing rules for that user. // Walked per request; trivially small in practice. Wildcards are NOT u_everyone- // only -- any user can create them. -type aclCache struct { +type accessCache struct { exact map[string]map[string]aclEntry pattern map[string][]aclEntry mu sync.RWMutex // Protect exact and pattern @@ -35,7 +35,7 @@ type aclCache struct { // // pattern is the pre-compiled regex equivalent of the stored LIKE pattern. // For exact-match entries (no % in the stored value) pattern is nil and the -// entry is reachable only through aclCache.exact[username][topic]. +// entry is reachable only through accessCache.exact[username][topic]. type aclEntry struct { length int // len() of the original stored topic/pattern pattern *regexp.Regexp // nil for exact entries @@ -43,8 +43,8 @@ type aclEntry struct { write bool } -func newAccessCache() *aclCache { - return &aclCache{ +func newAccessCache() *accessCache { + return &accessCache{ exact: make(map[string]map[string]aclEntry), pattern: make(map[string][]aclEntry), } @@ -54,7 +54,7 @@ func newAccessCache() *aclCache { // exact and pattern maps under the write lock. The primary is used (not // ReadOnly) so a reload immediately after an ACL mutation sees the freshly- // written rows without replica lag. -func (c *aclCache) reload(d *db.DB, query string) error { +func (c *accessCache) reload(d *db.DB, query string) error { rows, err := d.Query(query) if err != nil { return err @@ -98,7 +98,7 @@ func (c *aclCache) reload(d *db.DB, query string) error { // 1. specific user beats Everyone // 2. longer pattern beats shorter (more specific wins) // 3. write beats read at equal length (write is "stronger") -func (c *aclCache) Lookup(usernameOrEveryone, topic string) (read, write, found bool) { +func (c *accessCache) Lookup(usernameOrEveryone, topic string) (read, write, found bool) { escapedTopic := escapeUnderscore(topic) c.mu.RLock() defer c.mu.RUnlock() @@ -124,7 +124,7 @@ func (c *aclCache) Lookup(usernameOrEveryone, topic string) (read, write, found // Exact and wildcard rules are ranked together under the same criteria, so // an exact "foo" (length 3) beats a wildcard "f%" (length 2), but a wildcard // "foo%" (length 4) beats an exact "foo" (length 3). -func (c *aclCache) pickBestNoLock(username, topic, escapedTopic string) (*aclEntry, bool) { +func (c *accessCache) pickBestNoLock(username, topic, escapedTopic string) (*aclEntry, bool) { var best aclEntry var found bool if exact, exists := c.exact[username]; exists { diff --git a/user/access_cache_test.go b/user/access_cache_test.go index 90839872..65c109cb 100644 --- a/user/access_cache_test.go +++ b/user/access_cache_test.go @@ -140,8 +140,8 @@ func TestACLCache_SpecificUserBeatsEveryoneEvenWhenShorter(t *testing.T) { // Everyone with a longer matching rule. c := newAccessCache() loadCache(t, c, []rawACLRow{ - {user: Everyone, topic: "foo", read: true, write: true}, // exact, length 3 - {user: "phil", topic: "f%", read: false, write: false}, // wildcard, length 2, deny-all + {user: Everyone, topic: "foo", read: true, write: true}, // exact, length 3 + {user: "phil", topic: "f%", read: false, write: false}, // wildcard, length 2, deny-all }) read, write, found := c.Lookup("phil", "foo") require.True(t, found) @@ -155,8 +155,8 @@ func TestACLCache_SpecificUserBeatsEveryoneRegardlessOfWrite(t *testing.T) { // stronger, in either direction). c := newAccessCache() loadCache(t, c, []rawACLRow{ - {user: Everyone, topic: "mytopic", read: true, write: true}, // wide-open - {user: "phil", topic: "mytopic", read: true, write: false}, // read-only for phil + {user: Everyone, topic: "mytopic", read: true, write: true}, // wide-open + {user: "phil", topic: "mytopic", read: true, write: false}, // read-only for phil }) read, write, found := c.Lookup("phil", "mytopic") require.True(t, found) @@ -197,8 +197,8 @@ func TestACLCache_ExactBeatsShorterWildcardSameUser(t *testing.T) { // shorter wildcard could overwrite a longer exact. c := newAccessCache() loadCache(t, c, []rawACLRow{ - {user: "phil", topic: "foo", read: true, write: true}, // exact, length 3 - {user: "phil", topic: "f%", read: false, write: false}, // wildcard, length 2, deny-all + {user: "phil", topic: "foo", read: true, write: true}, // exact, length 3 + {user: "phil", topic: "f%", read: false, write: false}, // wildcard, length 2, deny-all }) read, write, found := c.Lookup("phil", "foo") require.True(t, found) @@ -212,8 +212,8 @@ func TestACLCache_LongerWildcardBeatsExactSameUser(t *testing.T) { // to wildcard when better() returns true" path. c := newAccessCache() loadCache(t, c, []rawACLRow{ - {user: "phil", topic: "foo", read: false, write: false}, // exact, length 3, deny-all - {user: "phil", topic: "foo%", read: true, write: true}, // wildcard, length 4 + {user: "phil", topic: "foo", read: false, write: false}, // exact, length 3, deny-all + {user: "phil", topic: "foo%", read: true, write: true}, // wildcard, length 4 }) read, write, found := c.Lookup("phil", "foo") require.True(t, found) @@ -281,7 +281,7 @@ type rawACLRow struct { // loadCache writes the given rows into the cache under its write lock, // preserving the same exact/wildcard partitioning that reload would produce. -func loadCache(t *testing.T, c *aclCache, rows []rawACLRow) { +func loadCache(t *testing.T, c *accessCache, rows []rawACLRow) { t.Helper() exact := make(map[string]map[string]aclEntry) wildcards := make(map[string][]aclEntry) diff --git a/user/manager.go b/user/manager.go index 52217e29..f9995e84 100644 --- a/user/manager.go +++ b/user/manager.go @@ -58,7 +58,7 @@ type Manager struct { queries queries statsQueue map[string]*Stats // "Queue" to asynchronously write user stats to the database (UserID -> Stats) tokenQueue map[string]*TokenUpdate // "Queue" to asynchronously write token access stats to the database (Token ID -> TokenUpdate) - accessCache *aclCache // In-memory snapshot of user_access; rebuilt after every ACL mutation + accessCache *accessCache // In-memory snapshot of user_access; rebuilt after every ACL mutation quit chan struct{} // Closed by Close() to signal background goroutines to stop mu sync.Mutex }