Monorepo for Tangled
Something went wrong. Try again.
4.5 kB · 203 lines
Go
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204package serververify
import ( "context" "errors" "fmt" "net" "net/http" "syscall" "time"
indigoxrpc "github.com/bluesky-social/indigo/xrpc" "tangled.org/core/api/tangled" "tangled.org/core/appview/db" "tangled.org/core/orm" "tangled.org/core/rbac" "tangled.org/core/xrpc/xrpcclient")
var ( FetchError = errors.New("failed to fetch owner"))
const verifyTimeout = 10 * time.Second
// fetchOwner fetches the owner DID from a server's /owner endpointfunc fetchOwner(ctx context.Context, domain string, dev bool) (string, error) { scheme := "https" if dev { scheme = "http" }
host := fmt.Sprintf("%s://%s", scheme, domain) transport := &http.Transport{ DialContext: safeDialer(dev).DialContext, } xrpcc := &indigoxrpc.Client{ Host: host, Client: &http.Client{ Timeout: verifyTimeout, Transport: transport, }, }
res, err := tangled.Owner(ctx, xrpcc) if err != nil { var xrpcerr *indigoxrpc.Error if !errors.As(err, &xrpcerr) { return "", err } if handled := xrpcclient.HandleXrpcErr(err); handled != nil { return "", handled } }
return res.Owner, nil}
type OwnerMismatch struct { expected string observed string}
func (e *OwnerMismatch) Error() string { return fmt.Sprintf("owner mismatch: %q != %q", e.expected, e.observed)}
// RunVerification verifies that the server at the given domain has the expected ownerfunc RunVerification(ctx context.Context, domain, expectedOwner string, dev bool) error { observedOwner, err := fetchOwner(ctx, domain, dev) if err != nil { return err }
if observedOwner != expectedOwner { return &OwnerMismatch{ expected: expectedOwner, observed: observedOwner, } }
return nil}
// MarkSpindleVerified marks a spindle as verified in the DB and adds the user as its ownerfunc MarkSpindleVerified(d *db.DB, e *rbac.Enforcer, instance, owner string) (int64, error) { tx, err := d.Begin() if err != nil { return 0, fmt.Errorf("failed to create txn: %w", err) } committed := false defer func() { if committed { return } tx.Rollback() e.E.LoadPolicy() }()
// mark this spindle as verified in the db rowId, err := db.VerifySpindle( tx, orm.FilterEq("owner", owner), orm.FilterEq("instance", instance), ) if err != nil { return 0, fmt.Errorf("failed to write to DB: %w", err) }
err = e.AddSpindleOwner(instance, owner) if err != nil { return 0, fmt.Errorf("failed to update ACL: %w", err) }
err = tx.Commit() if err != nil { return 0, fmt.Errorf("failed to commit txn: %w", err) }
err = e.E.SavePolicy() if err != nil { return 0, fmt.Errorf("failed to update ACL: %w", err) } committed = true
return rowId, nil}
// MarkKnotVerified marks a knot as verified and sets up ownership/permissionsfunc MarkKnotVerified(d *db.DB, e *rbac.Enforcer, domain, owner string) error { tx, err := d.BeginTx(context.Background(), nil) if err != nil { return fmt.Errorf("failed to start tx: %w", err) } committed := false defer func() { if committed { return } tx.Rollback() e.E.LoadPolicy() }()
// mark as registered err = db.MarkRegistered( tx, orm.FilterEq("did", owner), orm.FilterEq("domain", domain), ) if err != nil { return fmt.Errorf("failed to register domain: %w", err) }
// add basic acls for this domain err = e.AddKnot(domain) if err != nil { return fmt.Errorf("failed to add knot to enforcer: %w", err) }
// add this did as owner of this domain err = e.AddKnotOwner(domain, owner) if err != nil { return fmt.Errorf("failed to add knot owner to enforcer: %w", err) }
err = tx.Commit() if err != nil { return fmt.Errorf("failed to commit changes: %w", err) }
err = e.E.SavePolicy() if err != nil { return fmt.Errorf("failed to update ACLs: %w", err) } committed = true
return nil}func safeDialer(dev bool) *net.Dialer { d := &net.Dialer{ Timeout: 5 * time.Second, KeepAlive: 30 * time.Second, } if dev { return d } d.Control = func(network, address string, _ syscall.RawConn) error { host, _, err := net.SplitHostPort(address) if err != nil { return fmt.Errorf("invalid dial address %q: %w", address, err) } ip := net.ParseIP(host) if ip == nil { return fmt.Errorf("dial address %q did not resolve to IP", address) } if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsMulticast() || ip.IsUnspecified() { return fmt.Errorf("refusing to dial %s: reserved or private address", ip) } return nil } return d}