nip86: add auth checks.

This commit is contained in:
fiatjaf 2024-07-09 00:11:07 -03:00
parent 535f4c90e0
commit 91c652ef48
2 changed files with 87 additions and 37 deletions

117
nip86.go
View File

@ -1,41 +1,78 @@
package khatru
import (
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"reflect"
"strings"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip86"
)
type RelayManagementAPI struct {
BanPubKey func(pubkey string, reason string) error
ListBannedPubKeys func() ([]nip86.PubKeyReason, error)
AllowPubKey func(pubkey string, reason string) error
ListAllowedPubKeys func() ([]nip86.PubKeyReason, error)
ListEventsNeedingModeration func() ([]nip86.IDReason, error)
AllowEvent func(id string, reason string) error
BanEvent func(id string, reason string) error
ListBannedEvents func() ([]nip86.IDReason, error)
ChangeRelayName func(name string) error
ChangeRelayDescription func(desc string) error
ChangeRelayIcon func(icon string) error
AllowKind func(kind int) error
DisallowKind func(kind int) error
ListAllowedKinds func() ([]int, error)
BlockIP func(ip net.IP, reason string) error
UnblockIP func(ip net.IP, reason string) error
ListBlockedIPs func() ([]nip86.IPReason, error)
RejectAPICall []func(ctx context.Context, mp nip86.MethodParams) (reject bool, msg string)
BanPubKey func(ctx context.Context, pubkey string, reason string) error
ListBannedPubKeys func(ctx context.Context) ([]nip86.PubKeyReason, error)
AllowPubKey func(ctx context.Context, pubkey string, reason string) error
ListAllowedPubKeys func(ctx context.Context) ([]nip86.PubKeyReason, error)
ListEventsNeedingModeration func(ctx context.Context) ([]nip86.IDReason, error)
AllowEvent func(ctx context.Context, id string, reason string) error
BanEvent func(ctx context.Context, id string, reason string) error
ListBannedEvents func(ctx context.Context) ([]nip86.IDReason, error)
ChangeRelayName func(ctx context.Context, name string) error
ChangeRelayDescription func(ctx context.Context, desc string) error
ChangeRelayIcon func(ctx context.Context, icon string) error
AllowKind func(ctx context.Context, kind int) error
DisallowKind func(ctx context.Context, kind int) error
ListAllowedKinds func(ctx context.Context) ([]int, error)
BlockIP func(ctx context.Context, ip net.IP, reason string) error
UnblockIP func(ctx context.Context, ip net.IP, reason string) error
ListBlockedIPs func(ctx context.Context) ([]nip86.IPReason, error)
}
func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/nostr+json+rpc")
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "empty request", 400)
return
}
payloadHash := sha256.Sum256(nil)
auth := r.Header.Get("Authorization")
spl := strings.Split(auth, "Nostr ")
if len(spl) != 2 {
http.Error(w, "missing auth", 403)
return
}
var evt nostr.Event
if evtj, err := base64.StdEncoding.DecodeString(spl[1]); err != nil {
http.Error(w, "invalid base64 auth", 403)
return
} else if err := json.Unmarshal(evtj, &evt); err != nil {
http.Error(w, "invalid auth event json", 403)
return
} else if ok, _ := evt.CheckSignature(); !ok {
http.Error(w, "invalid auth event", 403)
return
} else if pht := evt.Tags.GetFirst([]string{"payload", hex.EncodeToString(payloadHash[:])}); pht == nil {
http.Error(w, "invalid auth event payload hash", 403)
return
}
var req nip86.Request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
if err := json.Unmarshal(payload, &req); err != nil {
http.Error(w, "invalid json body", 400)
return
}
@ -47,6 +84,15 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
}
var resp nip86.Response
ctx := context.WithValue(r.Context(), nip86HeaderAuthKey, evt.PubKey)
for _, rac := range rl.ManagementAPI.RejectAPICall {
if reject, msg := rac(ctx, mp); reject {
resp.Error = msg
goto respond
}
}
if _, ok := mp.(nip86.SupportedMethods); ok {
mat := reflect.TypeOf(rl.ManagementAPI)
mav := reflect.ValueOf(rl.ManagementAPI)
@ -69,7 +115,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.BanPubKey:
if rl.ManagementAPI.BanPubKey == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.BanPubKey(thing.PubKey, thing.Reason); err != nil {
} else if err := rl.ManagementAPI.BanPubKey(ctx, thing.PubKey, thing.Reason); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -77,7 +123,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ListBannedPubKeys:
if rl.ManagementAPI.ListBannedPubKeys == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if result, err := rl.ManagementAPI.ListBannedPubKeys(); err != nil {
} else if result, err := rl.ManagementAPI.ListBannedPubKeys(ctx); err != nil {
resp.Error = err.Error()
} else {
resp.Result = result
@ -85,7 +131,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.AllowPubKey:
if rl.ManagementAPI.AllowPubKey == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.AllowPubKey(thing.PubKey, thing.Reason); err != nil {
} else if err := rl.ManagementAPI.AllowPubKey(ctx, thing.PubKey, thing.Reason); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -93,7 +139,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ListAllowedPubKeys:
if rl.ManagementAPI.ListAllowedPubKeys == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if result, err := rl.ManagementAPI.ListAllowedPubKeys(); err != nil {
} else if result, err := rl.ManagementAPI.ListAllowedPubKeys(ctx); err != nil {
resp.Error = err.Error()
} else {
resp.Result = result
@ -101,7 +147,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.BanEvent:
if rl.ManagementAPI.BanEvent == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.BanEvent(thing.ID, thing.Reason); err != nil {
} else if err := rl.ManagementAPI.BanEvent(ctx, thing.ID, thing.Reason); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -109,7 +155,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.AllowEvent:
if rl.ManagementAPI.AllowEvent == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.AllowEvent(thing.ID, thing.Reason); err != nil {
} else if err := rl.ManagementAPI.AllowEvent(ctx, thing.ID, thing.Reason); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -117,7 +163,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ListEventsNeedingModeration:
if rl.ManagementAPI.ListEventsNeedingModeration == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if result, err := rl.ManagementAPI.ListEventsNeedingModeration(); err != nil {
} else if result, err := rl.ManagementAPI.ListEventsNeedingModeration(ctx); err != nil {
resp.Error = err.Error()
} else {
resp.Result = result
@ -125,7 +171,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ListBannedEvents:
if rl.ManagementAPI.ListBannedEvents == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if result, err := rl.ManagementAPI.ListEventsNeedingModeration(); err != nil {
} else if result, err := rl.ManagementAPI.ListEventsNeedingModeration(ctx); err != nil {
resp.Error = err.Error()
} else {
resp.Result = result
@ -133,7 +179,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ChangeRelayName:
if rl.ManagementAPI.ChangeRelayName == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.ChangeRelayName(thing.Name); err != nil {
} else if err := rl.ManagementAPI.ChangeRelayName(ctx, thing.Name); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -141,7 +187,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ChangeRelayDescription:
if rl.ManagementAPI.ChangeRelayDescription == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.ChangeRelayDescription(thing.Description); err != nil {
} else if err := rl.ManagementAPI.ChangeRelayDescription(ctx, thing.Description); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -149,7 +195,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ChangeRelayIcon:
if rl.ManagementAPI.ChangeRelayIcon == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.ChangeRelayIcon(thing.IconURL); err != nil {
} else if err := rl.ManagementAPI.ChangeRelayIcon(ctx, thing.IconURL); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -157,7 +203,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.AllowKind:
if rl.ManagementAPI.AllowKind == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.AllowKind(thing.Kind); err != nil {
} else if err := rl.ManagementAPI.AllowKind(ctx, thing.Kind); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -165,7 +211,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.DisallowKind:
if rl.ManagementAPI.DisallowKind == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.DisallowKind(thing.Kind); err != nil {
} else if err := rl.ManagementAPI.DisallowKind(ctx, thing.Kind); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -173,7 +219,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ListAllowedKinds:
if rl.ManagementAPI.ListAllowedKinds == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if result, err := rl.ManagementAPI.ListAllowedKinds(); err != nil {
} else if result, err := rl.ManagementAPI.ListAllowedKinds(ctx); err != nil {
resp.Error = err.Error()
} else {
resp.Result = result
@ -181,7 +227,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.BlockIP:
if rl.ManagementAPI.BlockIP == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.BlockIP(thing.IP, thing.Reason); err != nil {
} else if err := rl.ManagementAPI.BlockIP(ctx, thing.IP, thing.Reason); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -189,7 +235,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.UnblockIP:
if rl.ManagementAPI.UnblockIP == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if err := rl.ManagementAPI.UnblockIP(thing.IP, thing.Reason); err != nil {
} else if err := rl.ManagementAPI.UnblockIP(ctx, thing.IP, thing.Reason); err != nil {
resp.Error = err.Error()
} else {
resp.Result = true
@ -197,7 +243,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
case nip86.ListBlockedIPs:
if rl.ManagementAPI.ListBlockedIPs == nil {
resp.Error = fmt.Sprintf("method %s not supported", thing.MethodName())
} else if result, err := rl.ManagementAPI.ListBlockedIPs(); err != nil {
} else if result, err := rl.ManagementAPI.ListBlockedIPs(ctx); err != nil {
resp.Error = err.Error()
} else {
resp.Result = result
@ -207,5 +253,6 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
}
}
respond:
json.NewEncoder(w).Encode(resp)
}

View File

@ -9,6 +9,7 @@ import (
const (
wsKey = iota
subscriptionIdKey
nip86HeaderAuthKey
)
func RequestAuth(ctx context.Context) {
@ -30,10 +31,12 @@ func GetConnection(ctx context.Context) *WebSocket {
}
func GetAuthed(ctx context.Context) string {
conn := GetConnection(ctx)
if conn != nil {
if conn := GetConnection(ctx); conn != nil {
return conn.AuthedPublicKey
}
if nip86Auth := ctx.Value(nip86HeaderAuthKey); nip86Auth != nil {
return nip86Auth.(string)
}
return ""
}