tinyrelaycommit

Commit fbc0c60c00d6

browse files at this commit | history

1diff --git a/internal/daemon/services.go b/internal/daemon/services.go
2index cdcf712..75eca5f 100644
3--- a/internal/daemon/services.go
4+++ b/internal/daemon/services.go
5@@ -1,12 +1,15 @@
6 package daemon
7
8 import (
9+ "bytes"
10 "context"
11 "encoding/json"
12+ "errors"
13 "io"
14 "net/http"
15 "net/url"
16 "strings"
17+ "time"
18
19 "github.com/FelineStateMachine/tinyrelay/internal/blob"
20 "github.com/FelineStateMachine/tinyrelay/internal/configport"
21@@ -27,18 +30,38 @@ func (t *Tenant) initServices(ctx context.Context) error {
22 var err error
23 t.blobs, err = blob.New(ctx, blob.Config{
24 Root: t.meta.Paths.Root, Store: t.store, Authorize: t.authorizeBlob,
25- CanRead: func(ctx context.Context, pubkey string, hashes []string) bool {
26+ CanRead: func(ctx context.Context, hash string, pubkeys []string) bool {
27 p := t.Policy()
28 if p.Reads == "open" {
29 return true
30 }
31- role, err := t.community.Role(ctx, pubkey)
32- return err == nil && role != ""
33+ for _, pubkey := range pubkeys {
34+ if p.Reads == "auth" {
35+ return true
36+ }
37+ role, err := t.community.Role(ctx, pubkey)
38+ if err == nil && role != "" {
39+ return true
40+ }
41+ }
42+ return false
43 }, IsOwner: func(pubkey string) bool { return pubkey == t.Policy().Owner },
44 })
45 if err != nil {
46 return err
47 }
48+ t.community.ConfigurePolicy(t.Policy)
49+ t.community.OnMembershipApplied(func(event.Event) {
50+ go func() {
51+ generated, err := t.records.PublishMembership(context.Background(), time.Now().Unix())
52+ if err != nil {
53+ return
54+ }
55+ for _, record := range generated {
56+ _ = t.generatedRecord(context.Background(), record)
57+ }
58+ }()
59+ })
60 t.sites, err = sites.New(sites.Config{
61 Store: t.store,
62 GetBlob: func(ctx context.Context, hash string) (sites.Blob, error) {
63@@ -62,10 +85,10 @@ func (t *Tenant) initServices(ctx context.Context) error {
64 }
65 t.config = configport.New(configport.ConfigStore{Store: t.store, Community: t.community, Policy: t.Policy, OnApplied: func(p policy.Policy) { t.replacePolicy(p) }})
66 transport := &replication.NostrTransport{Dialer: replication.WebsocketDialer{}}
67- t.replication, err = replication.NewService(replication.Config{Store: t.store, Policy: replication.Policy{Enabled: t.Policy().Delivery.Enabled, SelfPubKey: t.Policy().Owner}, CurrentPolicy: func() replication.Policy {
68+ t.replication, err = replication.NewService(replication.Config{Store: t.store, Policy: replication.Policy{Enabled: t.Policy().Delivery.Enabled, SelfPubKey: t.records.PublicKey()}, CurrentPolicy: func() replication.Policy {
69 p := t.Policy()
70- return replication.Policy{Enabled: p.Delivery.Enabled, SelfPubKey: p.Owner}
71- }, Delivery: transport, Pull: transport, Push: transport, DataDir: t.meta.Paths.Root, Ingest: t.ingest})
72+ return replication.Policy{Enabled: p.Delivery.Enabled, ReadMembersOnly: p.Reads != "open", SelfPubKey: t.records.PublicKey()}
73+ }, Directory: localDirectory{store: t.store}, Delivery: transport, Pull: transport, Push: transport, DataDir: t.meta.Paths.Root, Ingest: t.ingest})
74 if err != nil {
75 return err
76 }
77@@ -86,6 +109,25 @@ func (t *Tenant) initServices(ctx context.Context) error {
78 // backend adapts the management method signature to webui's form-oriented API.
79 type backend struct{ tenant *Tenant }
80
81+type localDirectory struct{ store *storage.Store }
82+
83+func (d localDirectory) WriteRelays(pubkey string) []string { return d.relays(pubkey, true) }
84+func (d localDirectory) ReadRelays(pubkey string) []string { return d.relays(pubkey, false) }
85+func (d localDirectory) relays(pubkey string, write bool) []string {
86+ rows, err := d.store.Query(context.Background(), event.Filter{Authors: []string{pubkey}, Kinds: []int{10002}, Tags: map[string][]string{}}, storage.QueryOptions{Now: time.Now().Unix(), Access: storage.Access{All: true}, Limit: 1})
87+ if err != nil || len(rows.Events) == 0 {
88+ return nil
89+ }
90+ result := make([]string, 0)
91+ for _, tag := range rows.Events[0].Tags {
92+ if len(tag) < 2 || tag[0] != "r" || (!write && len(tag) > 2 && tag[2] != "read") || (write && len(tag) > 2 && tag[2] == "read") {
93+ continue
94+ }
95+ result = append(result, tag[1])
96+ }
97+ return result
98+}
99+
100 func (b backend) Query(ctx context.Context, method string, params []json.RawMessage, actor string) (any, error) {
101 return b.tenant.Execute(ctx, actor, method, params)
102 }
103@@ -121,11 +163,42 @@ func (t *Tenant) siteReadAccess(r *http.Request) bool {
104 }
105 func (t *Tenant) authorizeBlob(r *http.Request, action blob.Action) (string, error) {
106 e, err := t.auth.WhoAsks(r.Header.Get("Authorization"), t.publicURL+r.URL.RequestURI(), r.Method, "", string(action), strings.TrimPrefix(r.URL.Path, "/"))
107- return first(e), err
108+ pubkey := first(e)
109+ if err != nil {
110+ return "", err
111+ }
112+ if pubkey == "" {
113+ return "", errors.New("auth-required: blob authorization required")
114+ }
115+ if action == blob.ActionUpload || action == blob.ActionMirror {
116+ if !t.Policy().Features.Files {
117+ return "", errors.New("restricted: file uploads are disabled")
118+ }
119+ role, roleErr := t.community.Role(r.Context(), pubkey)
120+ if roleErr != nil || (t.Policy().Writes != "open" && role == "") {
121+ return "", errors.New("restricted: file upload is not allowed")
122+ }
123+ }
124+ return pubkey, nil
125 }
126 func (t *Tenant) authorizeGit(ctx context.Context, r *http.Request, repo gitrelay.Repository) error {
127- _, err := t.auth.VerifyNIP98(r.Header.Get("Authorization"), t.publicURL+r.URL.RequestURI(), r.Method, "")
128- return err
129+ body, err := io.ReadAll(r.Body)
130+ if err != nil {
131+ return err
132+ }
133+ r.Body = io.NopCloser(bytes.NewReader(body))
134+ e, err := t.auth.VerifyNIP98(r.Header.Get("Authorization"), t.publicURL+r.URL.RequestURI(), r.Method, string(body))
135+ if err != nil {
136+ return err
137+ }
138+ if e.PubKey == repo.Owner {
139+ return nil
140+ }
141+ role, roleErr := t.community.Role(ctx, e.PubKey)
142+ if roleErr != nil || (role != "owner" && role != "moderator" && role != "member") {
143+ return errors.New("restricted: repository authorization required")
144+ }
145+ return nil
146 }
147 func first(v []string) string {
148 if len(v) == 0 {
149@@ -134,7 +207,12 @@ func first(v []string) string {
150 return v[0]
151 }
152 func (t *Tenant) resolveUIActor(r *http.Request) (string, error) {
153- e, err := t.auth.VerifyNIP98(r.Header.Get("Authorization"), t.publicURL+r.URL.RequestURI(), r.Method, "")
154+ body, err := io.ReadAll(r.Body)
155+ if err != nil {
156+ return "", err
157+ }
158+ r.Body = io.NopCloser(bytes.NewReader(body))
159+ e, err := t.auth.VerifyNIP98(r.Header.Get("Authorization"), t.publicURL+r.URL.RequestURI(), r.Method, string(body))
160 if err != nil {
161 return "", err
162 }

About

ownerc01503e8035f
visibilitypublic
headfbc0c60c00d6

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs