You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
16 KiB
645 lines
16 KiB
package database |
|
|
|
import ( |
|
"bytes" |
|
"encoding/json" |
|
"fmt" |
|
"time" |
|
|
|
"github.com/dgraph-io/badger/v4" |
|
) |
|
|
|
// ManagedACLConfig represents the configuration for managed ACL mode |
|
type ManagedACLConfig struct { |
|
RelayName string `json:"relay_name"` |
|
RelayDescription string `json:"relay_description"` |
|
RelayIcon string `json:"relay_icon"` |
|
} |
|
|
|
// BannedPubkey represents a banned public key entry |
|
type BannedPubkey struct { |
|
Pubkey string `json:"pubkey"` |
|
Reason string `json:"reason,omitempty"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// AllowedPubkey represents an allowed public key entry |
|
type AllowedPubkey struct { |
|
Pubkey string `json:"pubkey"` |
|
Reason string `json:"reason,omitempty"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// BannedEvent represents a banned event entry |
|
type BannedEvent struct { |
|
ID string `json:"id"` |
|
Reason string `json:"reason,omitempty"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// AllowedEvent represents an allowed event entry |
|
type AllowedEvent struct { |
|
ID string `json:"id"` |
|
Reason string `json:"reason,omitempty"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// BlockedIP represents a blocked IP address entry |
|
type BlockedIP struct { |
|
IP string `json:"ip"` |
|
Reason string `json:"reason,omitempty"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// AllowedKind represents an allowed event kind |
|
type AllowedKind struct { |
|
Kind int `json:"kind"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// EventNeedingModeration represents an event that needs moderation |
|
type EventNeedingModeration struct { |
|
ID string `json:"id"` |
|
Reason string `json:"reason,omitempty"` |
|
Added time.Time `json:"added"` |
|
} |
|
|
|
// ManagedACL database operations |
|
type ManagedACL struct { |
|
*D |
|
} |
|
|
|
// NewManagedACL creates a new ManagedACL instance |
|
func NewManagedACL(db *D) *ManagedACL { |
|
return &ManagedACL{D: db} |
|
} |
|
|
|
// SaveBannedPubkey saves a banned pubkey to the database |
|
func (m *ManagedACL) SaveBannedPubkey(pubkey string, reason string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getBannedPubkeyKey(pubkey) |
|
banned := BannedPubkey{ |
|
Pubkey: pubkey, |
|
Reason: reason, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(banned) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveBannedPubkey removes a banned pubkey from the database |
|
func (m *ManagedACL) RemoveBannedPubkey(pubkey string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getBannedPubkeyKey(pubkey) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListBannedPubkeys returns all banned pubkeys |
|
func (m *ManagedACL) ListBannedPubkeys() ([]BannedPubkey, error) { |
|
var banned []BannedPubkey |
|
return banned, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getBannedPubkeyPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var bannedPubkey BannedPubkey |
|
if err := json.Unmarshal(val, &bannedPubkey); err != nil { |
|
continue |
|
} |
|
banned = append(banned, bannedPubkey) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveAllowedPubkey saves an allowed pubkey to the database |
|
func (m *ManagedACL) SaveAllowedPubkey(pubkey string, reason string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getAllowedPubkeyKey(pubkey) |
|
allowed := AllowedPubkey{ |
|
Pubkey: pubkey, |
|
Reason: reason, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(allowed) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveAllowedPubkey removes an allowed pubkey from the database |
|
func (m *ManagedACL) RemoveAllowedPubkey(pubkey string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getAllowedPubkeyKey(pubkey) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListAllowedPubkeys returns all allowed pubkeys |
|
func (m *ManagedACL) ListAllowedPubkeys() ([]AllowedPubkey, error) { |
|
var allowed []AllowedPubkey |
|
return allowed, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getAllowedPubkeyPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var allowedPubkey AllowedPubkey |
|
if err := json.Unmarshal(val, &allowedPubkey); err != nil { |
|
continue |
|
} |
|
allowed = append(allowed, allowedPubkey) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveBannedEvent saves a banned event to the database |
|
func (m *ManagedACL) SaveBannedEvent(eventID string, reason string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getBannedEventKey(eventID) |
|
banned := BannedEvent{ |
|
ID: eventID, |
|
Reason: reason, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(banned) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveBannedEvent removes a banned event from the database |
|
func (m *ManagedACL) RemoveBannedEvent(eventID string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getBannedEventKey(eventID) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListBannedEvents returns all banned events |
|
func (m *ManagedACL) ListBannedEvents() ([]BannedEvent, error) { |
|
var banned []BannedEvent |
|
return banned, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getBannedEventPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var bannedEvent BannedEvent |
|
if err := json.Unmarshal(val, &bannedEvent); err != nil { |
|
continue |
|
} |
|
banned = append(banned, bannedEvent) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveAllowedEvent saves an allowed event to the database |
|
func (m *ManagedACL) SaveAllowedEvent(eventID string, reason string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getAllowedEventKey(eventID) |
|
allowed := AllowedEvent{ |
|
ID: eventID, |
|
Reason: reason, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(allowed) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveAllowedEvent removes an allowed event from the database |
|
func (m *ManagedACL) RemoveAllowedEvent(eventID string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getAllowedEventKey(eventID) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListAllowedEvents returns all allowed events |
|
func (m *ManagedACL) ListAllowedEvents() ([]AllowedEvent, error) { |
|
var allowed []AllowedEvent |
|
return allowed, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getAllowedEventPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var allowedEvent AllowedEvent |
|
if err := json.Unmarshal(val, &allowedEvent); err != nil { |
|
continue |
|
} |
|
allowed = append(allowed, allowedEvent) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveBlockedIP saves a blocked IP to the database |
|
func (m *ManagedACL) SaveBlockedIP(ip string, reason string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getBlockedIPKey(ip) |
|
blocked := BlockedIP{ |
|
IP: ip, |
|
Reason: reason, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(blocked) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveBlockedIP removes a blocked IP from the database |
|
func (m *ManagedACL) RemoveBlockedIP(ip string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getBlockedIPKey(ip) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListBlockedIPs returns all blocked IPs |
|
func (m *ManagedACL) ListBlockedIPs() ([]BlockedIP, error) { |
|
var blocked []BlockedIP |
|
return blocked, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getBlockedIPPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var blockedIP BlockedIP |
|
if err := json.Unmarshal(val, &blockedIP); err != nil { |
|
continue |
|
} |
|
blocked = append(blocked, blockedIP) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveAllowedKind saves an allowed kind to the database |
|
func (m *ManagedACL) SaveAllowedKind(kind int) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getAllowedKindKey(kind) |
|
allowed := AllowedKind{ |
|
Kind: kind, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(allowed) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveAllowedKind removes an allowed kind from the database |
|
func (m *ManagedACL) RemoveAllowedKind(kind int) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getAllowedKindKey(kind) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListAllowedKinds returns all allowed kinds |
|
func (m *ManagedACL) ListAllowedKinds() ([]int, error) { |
|
var kinds []int |
|
return kinds, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getAllowedKindPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var allowedKind AllowedKind |
|
if err := json.Unmarshal(val, &allowedKind); err != nil { |
|
continue |
|
} |
|
kinds = append(kinds, allowedKind.Kind) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveEventNeedingModeration saves an event that needs moderation |
|
func (m *ManagedACL) SaveEventNeedingModeration(eventID string, reason string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getEventNeedingModerationKey(eventID) |
|
event := EventNeedingModeration{ |
|
ID: eventID, |
|
Reason: reason, |
|
Added: time.Now(), |
|
} |
|
data, err := json.Marshal(event) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// RemoveEventNeedingModeration removes an event from moderation queue |
|
func (m *ManagedACL) RemoveEventNeedingModeration(eventID string) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getEventNeedingModerationKey(eventID) |
|
return txn.Delete(key) |
|
}) |
|
} |
|
|
|
// ListEventsNeedingModeration returns all events needing moderation |
|
func (m *ManagedACL) ListEventsNeedingModeration() ([]EventNeedingModeration, error) { |
|
var events []EventNeedingModeration |
|
return events, m.View(func(txn *badger.Txn) error { |
|
prefix := m.getEventNeedingModerationPrefix() |
|
it := txn.NewIterator(badger.IteratorOptions{Prefix: prefix}) |
|
defer it.Close() |
|
|
|
for it.Rewind(); it.Valid(); it.Next() { |
|
item := it.Item() |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
continue |
|
} |
|
var event EventNeedingModeration |
|
if err := json.Unmarshal(val, &event); err != nil { |
|
continue |
|
} |
|
events = append(events, event) |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
// SaveRelayConfig saves relay configuration |
|
func (m *ManagedACL) SaveRelayConfig(config ManagedACLConfig) error { |
|
return m.Update(func(txn *badger.Txn) error { |
|
key := m.getRelayConfigKey() |
|
data, err := json.Marshal(config) |
|
if err != nil { |
|
return err |
|
} |
|
return txn.Set(key, data) |
|
}) |
|
} |
|
|
|
// GetRelayConfig returns relay configuration |
|
func (m *ManagedACL) GetRelayConfig() (ManagedACLConfig, error) { |
|
var config ManagedACLConfig |
|
return config, m.View(func(txn *badger.Txn) error { |
|
key := m.getRelayConfigKey() |
|
item, err := txn.Get(key) |
|
if err != nil { |
|
if err == badger.ErrKeyNotFound { |
|
// Return default config |
|
config = ManagedACLConfig{ |
|
RelayName: "Managed Relay", |
|
RelayDescription: "A managed Nostr relay", |
|
RelayIcon: "", |
|
} |
|
return nil |
|
} |
|
return err |
|
} |
|
val, err := item.ValueCopy(nil) |
|
if err != nil { |
|
return err |
|
} |
|
return json.Unmarshal(val, &config) |
|
}) |
|
} |
|
|
|
// Check if a pubkey is banned |
|
func (m *ManagedACL) IsPubkeyBanned(pubkey string) (bool, error) { |
|
var banned bool |
|
return banned, m.View(func(txn *badger.Txn) error { |
|
key := m.getBannedPubkeyKey(pubkey) |
|
_, err := txn.Get(key) |
|
if err == badger.ErrKeyNotFound { |
|
banned = false |
|
return nil |
|
} |
|
if err != nil { |
|
return err |
|
} |
|
banned = true |
|
return nil |
|
}) |
|
} |
|
|
|
// Check if a pubkey is explicitly allowed |
|
func (m *ManagedACL) IsPubkeyAllowed(pubkey string) (bool, error) { |
|
var allowed bool |
|
return allowed, m.View(func(txn *badger.Txn) error { |
|
key := m.getAllowedPubkeyKey(pubkey) |
|
_, err := txn.Get(key) |
|
if err == badger.ErrKeyNotFound { |
|
allowed = false |
|
return nil |
|
} |
|
if err != nil { |
|
return err |
|
} |
|
allowed = true |
|
return nil |
|
}) |
|
} |
|
|
|
// Check if an event is banned |
|
func (m *ManagedACL) IsEventBanned(eventID string) (bool, error) { |
|
var banned bool |
|
return banned, m.View(func(txn *badger.Txn) error { |
|
key := m.getBannedEventKey(eventID) |
|
_, err := txn.Get(key) |
|
if err == badger.ErrKeyNotFound { |
|
banned = false |
|
return nil |
|
} |
|
if err != nil { |
|
return err |
|
} |
|
banned = true |
|
return nil |
|
}) |
|
} |
|
|
|
// Check if an event is explicitly allowed |
|
func (m *ManagedACL) IsEventAllowed(eventID string) (bool, error) { |
|
var allowed bool |
|
return allowed, m.View(func(txn *badger.Txn) error { |
|
key := m.getAllowedEventKey(eventID) |
|
_, err := txn.Get(key) |
|
if err == badger.ErrKeyNotFound { |
|
allowed = false |
|
return nil |
|
} |
|
if err != nil { |
|
return err |
|
} |
|
allowed = true |
|
return nil |
|
}) |
|
} |
|
|
|
// Check if an IP is blocked |
|
func (m *ManagedACL) IsIPBlocked(ip string) (bool, error) { |
|
var blocked bool |
|
return blocked, m.View(func(txn *badger.Txn) error { |
|
key := m.getBlockedIPKey(ip) |
|
_, err := txn.Get(key) |
|
if err == badger.ErrKeyNotFound { |
|
blocked = false |
|
return nil |
|
} |
|
if err != nil { |
|
return err |
|
} |
|
blocked = true |
|
return nil |
|
}) |
|
} |
|
|
|
// Check if a kind is allowed |
|
func (m *ManagedACL) IsKindAllowed(kind int) (bool, error) { |
|
var allowed bool |
|
return allowed, m.View(func(txn *badger.Txn) error { |
|
key := m.getAllowedKindKey(kind) |
|
_, err := txn.Get(key) |
|
if err == badger.ErrKeyNotFound { |
|
allowed = false |
|
return nil |
|
} |
|
if err != nil { |
|
return err |
|
} |
|
allowed = true |
|
return nil |
|
}) |
|
} |
|
|
|
// Key generation methods |
|
func (m *ManagedACL) getBannedPubkeyKey(pubkey string) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_BANNED_PUBKEY_") |
|
buf.WriteString(pubkey) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getBannedPubkeyPrefix() []byte { |
|
return []byte("MANAGED_ACL_BANNED_PUBKEY_") |
|
} |
|
|
|
func (m *ManagedACL) getAllowedPubkeyKey(pubkey string) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_ALLOWED_PUBKEY_") |
|
buf.WriteString(pubkey) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getAllowedPubkeyPrefix() []byte { |
|
return []byte("MANAGED_ACL_ALLOWED_PUBKEY_") |
|
} |
|
|
|
func (m *ManagedACL) getBannedEventKey(eventID string) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_BANNED_EVENT_") |
|
buf.WriteString(eventID) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getBannedEventPrefix() []byte { |
|
return []byte("MANAGED_ACL_BANNED_EVENT_") |
|
} |
|
|
|
func (m *ManagedACL) getAllowedEventKey(eventID string) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_ALLOWED_EVENT_") |
|
buf.WriteString(eventID) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getAllowedEventPrefix() []byte { |
|
return []byte("MANAGED_ACL_ALLOWED_EVENT_") |
|
} |
|
|
|
func (m *ManagedACL) getBlockedIPKey(ip string) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_BLOCKED_IP_") |
|
buf.WriteString(ip) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getBlockedIPPrefix() []byte { |
|
return []byte("MANAGED_ACL_BLOCKED_IP_") |
|
} |
|
|
|
func (m *ManagedACL) getAllowedKindKey(kind int) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_ALLOWED_KIND_") |
|
buf.WriteString(fmt.Sprintf("%d", kind)) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getAllowedKindPrefix() []byte { |
|
return []byte("MANAGED_ACL_ALLOWED_KIND_") |
|
} |
|
|
|
func (m *ManagedACL) getEventNeedingModerationKey(eventID string) []byte { |
|
buf := new(bytes.Buffer) |
|
buf.WriteString("MANAGED_ACL_MODERATION_") |
|
buf.WriteString(eventID) |
|
return buf.Bytes() |
|
} |
|
|
|
func (m *ManagedACL) getEventNeedingModerationPrefix() []byte { |
|
return []byte("MANAGED_ACL_MODERATION_") |
|
} |
|
|
|
func (m *ManagedACL) getRelayConfigKey() []byte { |
|
return []byte("MANAGED_ACL_RELAY_CONFIG") |
|
}
|
|
|