Skip to content

Commit

Permalink
policies merged in, aliases moved to separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
mleku committed Jan 3, 2024
1 parent e0637e4 commit 57c05b3
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 118 deletions.
38 changes: 0 additions & 38 deletions cmd/replicatrd/policies/nip04.go

This file was deleted.

39 changes: 39 additions & 0 deletions cmd/replicatrd/replicatr/alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package replicatr

import (
"context"
"net/http"
"sync"

"github.com/fasthttp/websocket"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip11"
"github.com/puzpuzpuz/xsync/v2"
)

// aliases so we can swap out to another package with only here changed
type (
Ctx = context.Context
Info = nip11.RelayInformationDocument
Event = nostr.Event
Filter = nostr.Filter
Filters = nostr.Filters
TagMap = nostr.TagMap
EventEnvelope = nostr.EventEnvelope
OKEnvelope = nostr.OKEnvelope
CountEnvelope = nostr.CountEnvelope
ClosedEnvelope = nostr.ClosedEnvelope
ReqEnvelope = nostr.ReqEnvelope
EOSEEnvelope = nostr.EOSEEnvelope
CloseEnvelope = nostr.CloseEnvelope
AuthEnvelope = nostr.AuthEnvelope
NoticeEnvelope = nostr.NoticeEnvelope
Conn = websocket.Conn
Request = http.Request
ResponseWriter = http.ResponseWriter
Mutex = sync.Mutex
WaitGroup = sync.WaitGroup
CancelCauseFunc = context.CancelCauseFunc
ListenerMap = *xsync.MapOf[string, *Listener]
Timestamp = nostr.Timestamp
)
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package policies
package replicatr

import (
"context"

"github.com/nbd-wtf/go-nostr"
"golang.org/x/exp/slices"
)

// PreventTooManyIndexableTags returns a function that can be used as a RejectFilter that will reject
// events with more indexable (single-character) tags than the specified number.
// PreventTooManyIndexableTags returns a function that can be used as a
// RejectFilter that will reject events with more indexable (single-character)
// tags than the specified number.
//
// If ignoreKinds is given this restriction will not apply to these kinds (useful for allowing a bigger).
// If onlyKinds is given then all other kinds will be ignored.
func PreventTooManyIndexableTags(max int, ignoreKinds []int, onlyKinds []int) func(context.Context, *nostr.Event) (bool, string) {
// If ignoreKinds is given this restriction will not apply to these kinds
// (useful for allowing a bigger). If onlyKinds is given then all other kinds
// will be ignored.
func PreventTooManyIndexableTags(max int, ignoreKinds []int,
onlyKinds []int) func(Ctx, *Event) (bool, string) {

ignore := func(kind int) bool { return false }
if len(ignoreKinds) > 0 {
ignore = func(kind int) bool {
Expand All @@ -27,7 +29,7 @@ func PreventTooManyIndexableTags(max int, ignoreKinds []int, onlyKinds []int) fu
}
}

return func(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
return func(ctx Ctx, event *Event) (reject bool, msg string) {
if ignore(event.Kind) {
return false, ""
}
Expand All @@ -45,9 +47,10 @@ func PreventTooManyIndexableTags(max int, ignoreKinds []int, onlyKinds []int) fu
}
}

// PreventLargeTags rejects events that have indexable tag values greater than maxTagValueLen.
func PreventLargeTags(maxTagValueLen int) func(context.Context, *nostr.Event) (bool, string) {
return func(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
// PreventLargeTags rejects events that have indexable tag values greater than
// maxTagValueLen.
func PreventLargeTags(maxTagValueLen int) func(Ctx, *Event) (bool, string) {
return func(ctx Ctx, event *Event) (reject bool, msg string) {
for _, tag := range event.Tags {
if len(tag) > 1 && len(tag[0]) == 1 {
if len(tag[1]) > maxTagValueLen {
Expand All @@ -59,30 +62,29 @@ func PreventLargeTags(maxTagValueLen int) func(context.Context, *nostr.Event) (b
}
}

// RestrictToSpecifiedKinds returns a function that can be used as a RejectFilter that will reject
// any events with kinds different than the specified ones.
func RestrictToSpecifiedKinds(kinds ...uint16) func(context.Context, *nostr.Event) (bool, string) {
max := 0
min := 0
// RestrictToSpecifiedKinds returns a function that can be used as a
// RejectFilter that will reject any events with kinds different than the
// specified ones.
func RestrictToSpecifiedKinds(kinds ...uint16) func(Ctx, *Event) (bool, string) {
kMax := 0
kMin := 0
for _, kind := range kinds {
if int(kind) > max {
max = int(kind)
if int(kind) > kMax {
kMax = int(kind)
}
if int(kind) < min {
min = int(kind)
if int(kind) < kMin {
kMin = int(kind)
}
}

return func(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
return func(ctx Ctx, event *Event) (reject bool, msg string) {
// these are cheap and very questionable optimizations, but they exist for a reason:
// we would have to ensure that the kind number is within the bounds of a uint16 anyway
if event.Kind > max {
if event.Kind > kMax {
return true, "event kind not allowed"
}
if event.Kind < min {
if event.Kind < kMin {
return true, "event kind not allowed"
}

// hopefully this map of uint16s is very fast
if _, allowed := slices.BinarySearch(kinds, uint16(event.Kind)); allowed {
return false, ""
Expand All @@ -91,17 +93,17 @@ func RestrictToSpecifiedKinds(kinds ...uint16) func(context.Context, *nostr.Even
}
}

func PreventTimestampsInThePast(thresholdSeconds nostr.Timestamp) func(context.Context, *nostr.Event) (bool, string) {
return func(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
func PreventTimestampsInThePast(thresholdSeconds Timestamp) func(Ctx, *Event) (bool, string) {
return func(ctx Ctx, event *Event) (reject bool, msg string) {
if nostr.Now()-event.CreatedAt > thresholdSeconds {
return true, "event too old"
}
return false, ""
}
}

func PreventTimestampsInTheFuture(thresholdSeconds nostr.Timestamp) func(context.Context, *nostr.Event) (bool, string) {
return func(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
func PreventTimestampsInTheFuture(thresholdSeconds Timestamp) func(Ctx, *Event) (bool, string) {
return func(ctx Ctx, event *Event) (reject bool, msg string) {
if event.CreatedAt-nostr.Now() > thresholdSeconds {
return true, "event too much in the future"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package policies
package replicatr

import (
"context"
Expand All @@ -8,18 +8,17 @@ import (
)

// NoComplexFilters disallows filters with more than 2 tags.
func NoComplexFilters(ctx context.Context, filter nostr.Filter) (reject bool, msg string) {
func NoComplexFilters(ctx Ctx, filter *Filter) (reject bool, msg string) {
items := len(filter.Tags) + len(filter.Kinds)

if items > 4 && len(filter.Tags) > 2 {
return true, "too many things to filter for"
}

return false, ""
}

// NoEmptyFilters disallows filters that don't have at least a tag, a kind, an author or an id.
func NoEmptyFilters(ctx context.Context, filter nostr.Filter) (reject bool, msg string) {
// NoEmptyFilters disallows filters that don't have at least a tag, a kind, an
// author or an id.
func NoEmptyFilters(ctx Ctx, filter *Filter) (reject bool, msg string) {
c := len(filter.Kinds) + len(filter.IDs) + len(filter.Authors)
for _, tagItems := range filter.Tags {
c += len(tagItems)
Expand All @@ -30,26 +29,26 @@ func NoEmptyFilters(ctx context.Context, filter nostr.Filter) (reject bool, msg
return false, ""
}

// AntiSyncBots tries to prevent people from syncing kind:1s from this relay to else by always
// requiring an author parameter at least.
func AntiSyncBots(ctx context.Context, filter nostr.Filter) (reject bool, msg string) {
// AntiSyncBots tries to prevent people from syncing kind:1s from this relay to
// else by always requiring an author parameter at least.
func AntiSyncBots(ctx Ctx, filter *Filter) (reject bool, msg string) {
return (len(filter.Kinds) == 0 || slices.Contains(filter.Kinds, 1)) &&
len(filter.Authors) == 0, "an author must be specified to get their kind:1 notes"
}

func NoSearchQueries(ctx context.Context, filter nostr.Filter) (reject bool, msg string) {
func NoSearchQueries(ctx context.Context, filter *Filter) (reject bool, msg string) {
if filter.Search != "" {
return true, "search is not supported"
}
return false, ""
}

func RemoveSearchQueries(ctx context.Context, filter *nostr.Filter) {
func RemoveSearchQueries(ctx Ctx, filter *Filter) {
filter.Search = ""
}

func RemoveAllButKinds(kinds ...uint16) func(context.Context, *nostr.Filter) {
return func(ctx context.Context, filter *nostr.Filter) {
func RemoveAllButKinds(kinds ...uint16) func(Ctx, *nostr.Filter) {
return func(ctx Ctx, filter *Filter) {
if n := len(filter.Kinds); n > 0 {
newKinds := make([]int, 0, n)
for i := 0; i < n; i++ {
Expand All @@ -62,8 +61,8 @@ func RemoveAllButKinds(kinds ...uint16) func(context.Context, *nostr.Filter) {
}
}

func RemoveAllButTags(tagNames ...string) func(context.Context, *nostr.Filter) {
return func(ctx context.Context, filter *nostr.Filter) {
func RemoveAllButTags(tagNames ...string) func(Ctx, *Filter) {
return func(ctx Ctx, filter *Filter) {
for tagName := range filter.Tags {
if !slices.Contains(tagNames, tagName) {
delete(filter.Tags, tagName)
Expand Down
11 changes: 6 additions & 5 deletions cmd/replicatrd/replicatr/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func GetListeningFilters() (respFilters Filters) {
subs.Range(func(_ string, listener *Listener) bool {
for _, listenerFilter := range listener.filters {
for _, respFilter := range respFilters {
// check if this filter specifically is already added to respFilters
// check if this filter specifically is already added to
// respFilters
if nostr.FilterEqual(listenerFilter, respFilter) {
goto next
}
Expand All @@ -47,8 +48,8 @@ func setListener(id string, ws *WebSocket, f Filters, c CancelCauseFunc) {
subs.Store(id, &Listener{filters: f, cancel: c})
}

// remove a specific subscription id from listeners for a given ws client
// and cancel its specific context
// remove a specific subscription id from listeners for a given ws client and
// cancel its specific context
func removeListenerId(ws *WebSocket, id string) {
if subs, ok := listeners.Load(ws); ok {
if listener, ok := subs.LoadAndDelete(id); ok {
Expand All @@ -60,8 +61,8 @@ func removeListenerId(ws *WebSocket, id string) {
}
}

// remove WebSocket conn from listeners
// (no need to cancel contexts as they are all inherited from the main connection context)
// remove WebSocket conn from listeners (no need to cancel contexts as they are
// all inherited from the main connection context)
func removeListener(ws *WebSocket) { listeners.Delete(ws) }

func notifyListeners(event *Event) {
Expand Down
37 changes: 37 additions & 0 deletions cmd/replicatrd/replicatr/nip04.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package replicatr

import (
"golang.org/x/exp/slices"
)

// RejectKind04Snoopers prevents reading NIP-04 messages from people not
// involved in the conversation.
func RejectKind04Snoopers(ctx Ctx, filter *Filter) (bool, string) {
// prevent kind-4 events from being returned to unauthed users, only when
// authentication is a thing
if !slices.Contains(filter.Kinds, 4) {
return false, ""
}
ws := GetConnection(ctx)
s := filter.Authors
r, _ := filter.Tags["p"]
switch {
case ws.AuthedPublicKey == "":
// not authenticated
return true, "restricted: this relay does not serve kind-4 to " +
"unauthenticated users, does your client implement NIP-42?"
case len(s) == 1 && len(r) < 2 && (s[0] == ws.AuthedPublicKey):
// allowed filter: ws.authed is sole sender (filter specifies one or all
// r)
return false, ""
case len(r) == 1 && len(s) < 2 && (r[0] == ws.AuthedPublicKey):
// allowed filter: ws.authed is sole receiver (filter specifies one or
// all senders)
return false, ""
default:
// restricted filter: do not return any events, even if other elements
// in filters array were not restricted). client should know better.
return true, "restricted: authenticated user does not have " +
"authorization for requested filters."
}
}
30 changes: 0 additions & 30 deletions cmd/replicatrd/replicatr/relay.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package replicatr

import (
"context"
"net/http"
"os"
"sync"
"time"

log2 "github.com/Hubmakerlabs/replicatr/pkg/log"

"github.com/fasthttp/websocket"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip11"
"github.com/puzpuzpuz/xsync/v2"
)

Expand All @@ -24,32 +20,6 @@ const (
MaxMessageSize int64 = 512000 // ???
)

// aliases so we can swap out to another package with only here changed
type (
Ctx = context.Context
Info = nip11.RelayInformationDocument
Event = nostr.Event
Filter = nostr.Filter
Filters = nostr.Filters
TagMap = nostr.TagMap
EventEnvelope = nostr.EventEnvelope
OKEnvelope = nostr.OKEnvelope
CountEnvelope = nostr.CountEnvelope
ClosedEnvelope = nostr.ClosedEnvelope
ReqEnvelope = nostr.ReqEnvelope
EOSEEnvelope = nostr.EOSEEnvelope
CloseEnvelope = nostr.CloseEnvelope
AuthEnvelope = nostr.AuthEnvelope
NoticeEnvelope = nostr.NoticeEnvelope
Conn = websocket.Conn
Request = http.Request
ResponseWriter = http.ResponseWriter
Mutex = sync.Mutex
WaitGroup = sync.WaitGroup
CancelCauseFunc = context.CancelCauseFunc
ListenerMap = *xsync.MapOf[string, *Listener]
)

// function types used in the relay state
type (
RejectEvent func(ctx Ctx, event *Event) (reject bool, msg string)
Expand Down

0 comments on commit 57c05b3

Please sign in to comment.