mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-17 00:47:54 +00:00
Rename acl cahce
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
|||||||
"heckel.io/ntfy/v2/db"
|
"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
|
// 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
|
// 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.
|
// 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-
|
// Walked per request; trivially small in practice. Wildcards are NOT u_everyone-
|
||||||
// only -- any user can create them.
|
// only -- any user can create them.
|
||||||
type aclCache struct {
|
type accessCache struct {
|
||||||
exact map[string]map[string]aclEntry
|
exact map[string]map[string]aclEntry
|
||||||
pattern map[string][]aclEntry
|
pattern map[string][]aclEntry
|
||||||
mu sync.RWMutex // Protect exact and pattern
|
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.
|
// 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
|
// 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 {
|
type aclEntry struct {
|
||||||
length int // len() of the original stored topic/pattern
|
length int // len() of the original stored topic/pattern
|
||||||
pattern *regexp.Regexp // nil for exact entries
|
pattern *regexp.Regexp // nil for exact entries
|
||||||
@@ -43,8 +43,8 @@ type aclEntry struct {
|
|||||||
write bool
|
write bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAccessCache() *aclCache {
|
func newAccessCache() *accessCache {
|
||||||
return &aclCache{
|
return &accessCache{
|
||||||
exact: make(map[string]map[string]aclEntry),
|
exact: make(map[string]map[string]aclEntry),
|
||||||
pattern: make(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
|
// 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-
|
// ReadOnly) so a reload immediately after an ACL mutation sees the freshly-
|
||||||
// written rows without replica lag.
|
// 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)
|
rows, err := d.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -98,7 +98,7 @@ func (c *aclCache) reload(d *db.DB, query string) error {
|
|||||||
// 1. specific user beats Everyone
|
// 1. specific user beats Everyone
|
||||||
// 2. longer pattern beats shorter (more specific wins)
|
// 2. longer pattern beats shorter (more specific wins)
|
||||||
// 3. write beats read at equal length (write is "stronger")
|
// 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)
|
escapedTopic := escapeUnderscore(topic)
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
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
|
// 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
|
// an exact "foo" (length 3) beats a wildcard "f%" (length 2), but a wildcard
|
||||||
// "foo%" (length 4) beats an exact "foo" (length 3).
|
// "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 best aclEntry
|
||||||
var found bool
|
var found bool
|
||||||
if exact, exists := c.exact[username]; exists {
|
if exact, exists := c.exact[username]; exists {
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ func TestACLCache_SpecificUserBeatsEveryoneEvenWhenShorter(t *testing.T) {
|
|||||||
// Everyone with a longer matching rule.
|
// Everyone with a longer matching rule.
|
||||||
c := newAccessCache()
|
c := newAccessCache()
|
||||||
loadCache(t, c, []rawACLRow{
|
loadCache(t, c, []rawACLRow{
|
||||||
{user: Everyone, topic: "foo", read: true, write: true}, // exact, length 3
|
{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: "phil", topic: "f%", read: false, write: false}, // wildcard, length 2, deny-all
|
||||||
})
|
})
|
||||||
read, write, found := c.Lookup("phil", "foo")
|
read, write, found := c.Lookup("phil", "foo")
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
@@ -155,8 +155,8 @@ func TestACLCache_SpecificUserBeatsEveryoneRegardlessOfWrite(t *testing.T) {
|
|||||||
// stronger, in either direction).
|
// stronger, in either direction).
|
||||||
c := newAccessCache()
|
c := newAccessCache()
|
||||||
loadCache(t, c, []rawACLRow{
|
loadCache(t, c, []rawACLRow{
|
||||||
{user: Everyone, topic: "mytopic", read: true, write: true}, // wide-open
|
{user: Everyone, topic: "mytopic", read: true, write: true}, // wide-open
|
||||||
{user: "phil", topic: "mytopic", read: true, write: false}, // read-only for phil
|
{user: "phil", topic: "mytopic", read: true, write: false}, // read-only for phil
|
||||||
})
|
})
|
||||||
read, write, found := c.Lookup("phil", "mytopic")
|
read, write, found := c.Lookup("phil", "mytopic")
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
@@ -197,8 +197,8 @@ func TestACLCache_ExactBeatsShorterWildcardSameUser(t *testing.T) {
|
|||||||
// shorter wildcard could overwrite a longer exact.
|
// shorter wildcard could overwrite a longer exact.
|
||||||
c := newAccessCache()
|
c := newAccessCache()
|
||||||
loadCache(t, c, []rawACLRow{
|
loadCache(t, c, []rawACLRow{
|
||||||
{user: "phil", topic: "foo", read: true, write: true}, // exact, length 3
|
{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: "f%", read: false, write: false}, // wildcard, length 2, deny-all
|
||||||
})
|
})
|
||||||
read, write, found := c.Lookup("phil", "foo")
|
read, write, found := c.Lookup("phil", "foo")
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
@@ -212,8 +212,8 @@ func TestACLCache_LongerWildcardBeatsExactSameUser(t *testing.T) {
|
|||||||
// to wildcard when better() returns true" path.
|
// to wildcard when better() returns true" path.
|
||||||
c := newAccessCache()
|
c := newAccessCache()
|
||||||
loadCache(t, c, []rawACLRow{
|
loadCache(t, c, []rawACLRow{
|
||||||
{user: "phil", topic: "foo", read: false, write: false}, // exact, length 3, deny-all
|
{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: true, write: true}, // wildcard, length 4
|
||||||
})
|
})
|
||||||
read, write, found := c.Lookup("phil", "foo")
|
read, write, found := c.Lookup("phil", "foo")
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
@@ -281,7 +281,7 @@ type rawACLRow struct {
|
|||||||
|
|
||||||
// loadCache writes the given rows into the cache under its write lock,
|
// loadCache writes the given rows into the cache under its write lock,
|
||||||
// preserving the same exact/wildcard partitioning that reload would produce.
|
// 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()
|
t.Helper()
|
||||||
exact := make(map[string]map[string]aclEntry)
|
exact := make(map[string]map[string]aclEntry)
|
||||||
wildcards := make(map[string][]aclEntry)
|
wildcards := make(map[string][]aclEntry)
|
||||||
|
|||||||
+1
-1
@@ -58,7 +58,7 @@ type Manager struct {
|
|||||||
queries queries
|
queries queries
|
||||||
statsQueue map[string]*Stats // "Queue" to asynchronously write user stats to the database (UserID -> Stats)
|
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)
|
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
|
quit chan struct{} // Closed by Close() to signal background goroutines to stop
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user