forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
Monorepo for Tangled
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package serververify
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8 "net/http"
9 "strings"
10 "time"
11
12 "tangled.sh/tangled.sh/core/appview/db"
13 "tangled.sh/tangled.sh/core/rbac"
14)
15
16var (
17 FetchError = errors.New("failed to fetch owner")
18)
19
20// fetchOwner fetches the owner DID from a server's /owner endpoint
21func fetchOwner(ctx context.Context, domain string, dev bool) (string, error) {
22 scheme := "https"
23 if dev {
24 scheme = "http"
25 }
26
27 url := fmt.Sprintf("%s://%s/owner", scheme, domain)
28 req, err := http.NewRequest("GET", url, nil)
29 if err != nil {
30 return "", err
31 }
32
33 client := &http.Client{
34 Timeout: 1 * time.Second,
35 }
36
37 resp, err := client.Do(req.WithContext(ctx))
38 if err != nil || resp.StatusCode != 200 {
39 return "", fmt.Errorf("failed to fetch /owner")
40 }
41
42 body, err := io.ReadAll(io.LimitReader(resp.Body, 1024)) // read atmost 1kb of data
43 if err != nil {
44 return "", fmt.Errorf("failed to read /owner response: %w", err)
45 }
46
47 did := strings.TrimSpace(string(body))
48 if did == "" {
49 return "", fmt.Errorf("empty DID in /owner response")
50 }
51
52 return did, nil
53}
54
55type OwnerMismatch struct {
56 expected string
57 observed string
58}
59
60func (e *OwnerMismatch) Error() string {
61 return fmt.Sprintf("owner mismatch: %q != %q", e.expected, e.observed)
62}
63
64// RunVerification verifies that the server at the given domain has the expected owner
65func RunVerification(ctx context.Context, domain, expectedOwner string, dev bool) error {
66 observedOwner, err := fetchOwner(ctx, domain, dev)
67 if err != nil {
68 return fmt.Errorf("%w: %w", FetchError, err)
69 }
70
71 if observedOwner != expectedOwner {
72 return &OwnerMismatch{
73 expected: expectedOwner,
74 observed: observedOwner,
75 }
76 }
77
78 return nil
79}
80
81// MarkSpindleVerified marks a spindle as verified in the DB and adds the user as its owner
82func MarkSpindleVerified(d *db.DB, e *rbac.Enforcer, instance, owner string) (int64, error) {
83 tx, err := d.Begin()
84 if err != nil {
85 return 0, fmt.Errorf("failed to create txn: %w", err)
86 }
87 defer func() {
88 tx.Rollback()
89 e.E.LoadPolicy()
90 }()
91
92 // mark this spindle as verified in the db
93 rowId, err := db.VerifySpindle(
94 tx,
95 db.FilterEq("owner", owner),
96 db.FilterEq("instance", instance),
97 )
98 if err != nil {
99 return 0, fmt.Errorf("failed to write to DB: %w", err)
100 }
101
102 err = e.AddSpindleOwner(instance, owner)
103 if err != nil {
104 return 0, fmt.Errorf("failed to update ACL: %w", err)
105 }
106
107 err = tx.Commit()
108 if err != nil {
109 return 0, fmt.Errorf("failed to commit txn: %w", err)
110 }
111
112 err = e.E.SavePolicy()
113 if err != nil {
114 return 0, fmt.Errorf("failed to update ACL: %w", err)
115 }
116
117 return rowId, nil
118}
119
120// MarkKnotVerified marks a knot as verified and sets up ownership/permissions
121func MarkKnotVerified(d *db.DB, e *rbac.Enforcer, domain, owner string) error {
122 tx, err := d.BeginTx(context.Background(), nil)
123 if err != nil {
124 return fmt.Errorf("failed to start tx: %w", err)
125 }
126 defer func() {
127 tx.Rollback()
128 e.E.LoadPolicy()
129 }()
130
131 // mark as registered
132 err = db.MarkRegistered(
133 tx,
134 db.FilterEq("did", owner),
135 db.FilterEq("domain", domain),
136 )
137 if err != nil {
138 return fmt.Errorf("failed to register domain: %w", err)
139 }
140
141 // add basic acls for this domain
142 err = e.AddKnot(domain)
143 if err != nil {
144 return fmt.Errorf("failed to add knot to enforcer: %w", err)
145 }
146
147 // add this did as owner of this domain
148 err = e.AddKnotOwner(domain, owner)
149 if err != nil {
150 return fmt.Errorf("failed to add knot owner to enforcer: %w", err)
151 }
152
153 err = tx.Commit()
154 if err != nil {
155 return fmt.Errorf("failed to commit changes: %w", err)
156 }
157
158 err = e.E.SavePolicy()
159 if err != nil {
160 return fmt.Errorf("failed to update ACLs: %w", err)
161 }
162
163 return nil
164}