Files
next.orly.dev/pkg/database/managed-acl.go

648 lines
16 KiB
Go

//go:build !(js && wasm)
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")
}