forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at master 3.3 kB view raw
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 defer func() { 73 tx.Rollback() 74 e.E.LoadPolicy() 75 }() 76 77 // mark this spindle as verified in the db 78 rowId, err := db.VerifySpindle( 79 tx, 80 orm.FilterEq("owner", owner), 81 orm.FilterEq("instance", instance), 82 ) 83 if err != nil { 84 return 0, fmt.Errorf("failed to write to DB: %w", err) 85 } 86 87 err = e.AddSpindleOwner(instance, owner) 88 if err != nil { 89 return 0, fmt.Errorf("failed to update ACL: %w", err) 90 } 91 92 err = tx.Commit() 93 if err != nil { 94 return 0, fmt.Errorf("failed to commit txn: %w", err) 95 } 96 97 err = e.E.SavePolicy() 98 if err != nil { 99 return 0, fmt.Errorf("failed to update ACL: %w", err) 100 } 101 102 return rowId, nil 103} 104 105// MarkKnotVerified marks a knot as verified and sets up ownership/permissions 106func MarkKnotVerified(d *db.DB, e *rbac.Enforcer, domain, owner string) error { 107 tx, err := d.BeginTx(context.Background(), nil) 108 if err != nil { 109 return fmt.Errorf("failed to start tx: %w", err) 110 } 111 defer func() { 112 tx.Rollback() 113 e.E.LoadPolicy() 114 }() 115 116 // mark as registered 117 err = db.MarkRegistered( 118 tx, 119 orm.FilterEq("did", owner), 120 orm.FilterEq("domain", domain), 121 ) 122 if err != nil { 123 return fmt.Errorf("failed to register domain: %w", err) 124 } 125 126 // add basic acls for this domain 127 err = e.AddKnot(domain) 128 if err != nil { 129 return fmt.Errorf("failed to add knot to enforcer: %w", err) 130 } 131 132 // add this did as owner of this domain 133 err = e.AddKnotOwner(domain, owner) 134 if err != nil { 135 return fmt.Errorf("failed to add knot owner to enforcer: %w", err) 136 } 137 138 err = tx.Commit() 139 if err != nil { 140 return fmt.Errorf("failed to commit changes: %w", err) 141 } 142 143 err = e.E.SavePolicy() 144 if err != nil { 145 return fmt.Errorf("failed to update ACLs: %w", err) 146 } 147 148 return nil 149}