Monorepo for Tangled tangled.org
5

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 8 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/appview/db" 11 "tangled.org/core/appview/xrpcclient" 12 "tangled.org/core/orm" 13 "tangled.org/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 host := fmt.Sprintf("%s://%s", scheme, domain) 28 xrpcc := &indigoxrpc.Client{ 29 Host: host, 30 } 31 32 res, err := tangled.Owner(ctx, xrpcc) 33 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 34 return "", xrpcerr 35 } 36 37 return res.Owner, nil 38} 39 40type OwnerMismatch struct { 41 expected string 42 observed string 43} 44 45func (e *OwnerMismatch) Error() string { 46 return fmt.Sprintf("owner mismatch: %q != %q", e.expected, e.observed) 47} 48 49// RunVerification verifies that the server at the given domain has the expected owner 50func RunVerification(ctx context.Context, domain, expectedOwner string, dev bool) error { 51 observedOwner, err := fetchOwner(ctx, domain, dev) 52 if err != nil { 53 return err 54 } 55 56 if observedOwner != expectedOwner { 57 return &OwnerMismatch{ 58 expected: expectedOwner, 59 observed: observedOwner, 60 } 61 } 62 63 return nil 64} 65 66// MarkSpindleVerified marks a spindle as verified in the DB and adds the user as its owner 67func MarkSpindleVerified(d *db.DB, e *rbac.Enforcer, instance, owner string) (int64, error) { 68 tx, err := d.Begin() 69 if err != nil { 70 return 0, fmt.Errorf("failed to create txn: %w", err) 71 } 72 committed := false 73 defer func() { 74 if committed { 75 return 76 } 77 tx.Rollback() 78 e.E.LoadPolicy() 79 }() 80 81 // mark this spindle as verified in the db 82 rowId, err := db.VerifySpindle( 83 tx, 84 orm.FilterEq("owner", owner), 85 orm.FilterEq("instance", instance), 86 ) 87 if err != nil { 88 return 0, fmt.Errorf("failed to write to DB: %w", err) 89 } 90 91 err = e.AddSpindleOwner(instance, owner) 92 if err != nil { 93 return 0, fmt.Errorf("failed to update ACL: %w", err) 94 } 95 96 err = tx.Commit() 97 if err != nil { 98 return 0, fmt.Errorf("failed to commit txn: %w", err) 99 } 100 101 err = e.E.SavePolicy() 102 if err != nil { 103 return 0, fmt.Errorf("failed to update ACL: %w", err) 104 } 105 committed = true 106 107 return rowId, nil 108} 109 110// MarkKnotVerified marks a knot as verified and sets up ownership/permissions 111func MarkKnotVerified(d *db.DB, e *rbac.Enforcer, domain, owner string) error { 112 tx, err := d.BeginTx(context.Background(), nil) 113 if err != nil { 114 return fmt.Errorf("failed to start tx: %w", err) 115 } 116 committed := false 117 defer func() { 118 if committed { 119 return 120 } 121 tx.Rollback() 122 e.E.LoadPolicy() 123 }() 124 125 // mark as registered 126 err = db.MarkRegistered( 127 tx, 128 orm.FilterEq("did", owner), 129 orm.FilterEq("domain", domain), 130 ) 131 if err != nil { 132 return fmt.Errorf("failed to register domain: %w", err) 133 } 134 135 // add basic acls for this domain 136 err = e.AddKnot(domain) 137 if err != nil { 138 return fmt.Errorf("failed to add knot to enforcer: %w", err) 139 } 140 141 // add this did as owner of this domain 142 err = e.AddKnotOwner(domain, owner) 143 if err != nil { 144 return fmt.Errorf("failed to add knot owner to enforcer: %w", err) 145 } 146 147 err = tx.Commit() 148 if err != nil { 149 return fmt.Errorf("failed to commit changes: %w", err) 150 } 151 152 err = e.E.SavePolicy() 153 if err != nil { 154 return fmt.Errorf("failed to update ACLs: %w", err) 155 } 156 committed = true 157 158 return nil 159}