forked from hailey.at/cocoon
An atproto PDS written in Go

refactor identity package (#22)

authored by hailey.at and committed by GitHub 2face30c fead6747

Changed files
+89 -59
identity
+73 -54
identity/identity.go
··· 13 "github.com/bluesky-social/indigo/util" 14 ) 15 16 - func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 17 - if cli == nil { 18 - cli = util.RobustHTTPClient() 19 - } 20 - 21 - var did string 22 - 23 - _, err := syntax.ParseHandle(handle) 24 if err != nil { 25 - return "", err 26 } 27 28 - recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) 29 - if err == nil { 30 - for _, rec := range recs { 31 - if strings.HasPrefix(rec, "did=") { 32 - did = strings.Split(rec, "did=")[1] 33 - break 34 } 35 } 36 - } else { 37 - fmt.Printf("erorr getting txt records: %v\n", err) 38 } 39 40 - if did == "" { 41 - req, err := http.NewRequestWithContext( 42 - ctx, 43 - "GET", 44 - fmt.Sprintf("https://%s/.well-known/atproto-did", handle), 45 - nil, 46 - ) 47 - if err != nil { 48 - return "", nil 49 - } 50 51 - resp, err := http.DefaultClient.Do(req) 52 - if err != nil { 53 - return "", nil 54 - } 55 - defer resp.Body.Close() 56 57 - if resp.StatusCode != http.StatusOK { 58 - io.Copy(io.Discard, resp.Body) 59 - return "", fmt.Errorf("unable to resolve handle") 60 - } 61 62 - b, err := io.ReadAll(resp.Body) 63 - if err != nil { 64 - return "", err 65 - } 66 67 - maybeDid := string(b) 68 69 - if _, err := syntax.ParseDID(maybeDid); err != nil { 70 - return "", fmt.Errorf("unable to resolve handle") 71 - } 72 73 - did = maybeDid 74 } 75 76 - return did, nil 77 } 78 79 - func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 80 if cli == nil { 81 cli = util.RobustHTTPClient() 82 } 83 84 - var ustr string 85 if strings.HasPrefix(did, "did:plc:") { 86 - ustr = fmt.Sprintf("https://plc.directory/%s", did) 87 } else if strings.HasPrefix(did, "did:web:") { 88 - ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 89 } else { 90 - return nil, fmt.Errorf("did was not a supported did type") 91 } 92 93 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) ··· 95 return nil, err 96 } 97 98 - resp, err := http.DefaultClient.Do(req) 99 if err != nil { 100 return nil, err 101 } ··· 103 104 if resp.StatusCode != 200 { 105 io.Copy(io.Discard, resp.Body) 106 - return nil, fmt.Errorf("could not find identity in plc registry") 107 } 108 109 var diddoc DidDoc ··· 127 return nil, err 128 } 129 130 - resp, err := http.DefaultClient.Do(req) 131 if err != nil { 132 return nil, err 133 }
··· 13 "github.com/bluesky-social/indigo/util" 14 ) 15 16 + func ResolveHandleFromTXT(ctx context.Context, handle string) (string, error) { 17 + name := fmt.Sprintf("_atproto.%s", handle) 18 + recs, err := net.LookupTXT(name) 19 if err != nil { 20 + return "", fmt.Errorf("handle could not be resolved via txt: %w", err) 21 } 22 23 + for _, rec := range recs { 24 + if strings.HasPrefix(rec, "did=") { 25 + maybeDid := strings.Split(rec, "did=")[1] 26 + if _, err := syntax.ParseDID(maybeDid); err == nil { 27 + return maybeDid, nil 28 } 29 } 30 } 31 32 + return "", fmt.Errorf("handle could not be resolved via txt: no record found") 33 + } 34 35 + func ResolveHandleFromWellKnown(ctx context.Context, cli *http.Client, handle string) (string, error) { 36 + ustr := fmt.Sprintf("https://%s/.well=known/atproto-did", handle) 37 + req, err := http.NewRequestWithContext( 38 + ctx, 39 + "GET", 40 + ustr, 41 + nil, 42 + ) 43 + if err != nil { 44 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 45 + } 46 47 + resp, err := cli.Do(req) 48 + if err != nil { 49 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 50 + } 51 + defer resp.Body.Close() 52 53 + b, err := io.ReadAll(resp.Body) 54 + if err != nil { 55 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 56 + } 57 58 + if resp.StatusCode != http.StatusOK { 59 + return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode) 60 + } 61 62 + maybeDid := string(b) 63 64 + if _, err := syntax.ParseDID(maybeDid); err != nil { 65 + return "", fmt.Errorf("handle could not be resolved via web: invalid did in document") 66 } 67 68 + return maybeDid, nil 69 } 70 71 + func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 72 if cli == nil { 73 cli = util.RobustHTTPClient() 74 } 75 76 + _, err := syntax.ParseHandle(handle) 77 + if err != nil { 78 + return "", err 79 + } 80 + 81 + if maybeDidFromTxt, err := ResolveHandleFromTXT(ctx, handle); err == nil { 82 + return maybeDidFromTxt, nil 83 + } 84 + 85 + if maybeDidFromWeb, err := ResolveHandleFromWellKnown(ctx, cli, handle); err == nil { 86 + return maybeDidFromWeb, nil 87 + } 88 + 89 + return "", fmt.Errorf("handle could not be resolved") 90 + } 91 + 92 + func DidToDocUrl(did string) (string, error) { 93 if strings.HasPrefix(did, "did:plc:") { 94 + return fmt.Sprintf("https://plc.directory/%s", did), nil 95 } else if strings.HasPrefix(did, "did:web:") { 96 + return fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")), nil 97 } else { 98 + return "", fmt.Errorf("did was not a supported did type") 99 + } 100 + } 101 + 102 + func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 103 + if cli == nil { 104 + cli = util.RobustHTTPClient() 105 + } 106 + 107 + ustr, err := DidToDocUrl(did) 108 + if err != nil { 109 + return nil, err 110 } 111 112 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) ··· 114 return nil, err 115 } 116 117 + resp, err := cli.Do(req) 118 if err != nil { 119 return nil, err 120 } ··· 122 123 if resp.StatusCode != 200 { 124 io.Copy(io.Discard, resp.Body) 125 + return nil, fmt.Errorf("unable to find did doc at url. did: %s. url: %s", did, ustr) 126 } 127 128 var diddoc DidDoc ··· 146 return nil, err 147 } 148 149 + resp, err := cli.Do(req) 150 if err != nil { 151 return nil, err 152 }
+16 -5
identity/passport.go
··· 19 type Passport struct { 20 h *http.Client 21 bc BackingCache 22 - lk sync.Mutex 23 } 24 25 func NewPassport(h *http.Client, bc BackingCache) *Passport { ··· 30 return &Passport{ 31 h: h, 32 bc: bc, 33 - lk: sync.Mutex{}, 34 } 35 } 36 ··· 38 skipCache, _ := ctx.Value("skip-cache").(bool) 39 40 if !skipCache { 41 cached, ok := p.bc.GetDoc(did) 42 if ok { 43 return cached, nil 44 } 45 } 46 47 - p.lk.Lock() // this is pretty pathetic, and i should rethink this. but for now, fuck it 48 - defer p.lk.Unlock() 49 - 50 doc, err := FetchDidDoc(ctx, p.h, did) 51 if err != nil { 52 return nil, err 53 } 54 55 p.bc.PutDoc(did, doc) 56 57 return doc, nil 58 } ··· 61 skipCache, _ := ctx.Value("skip-cache").(bool) 62 63 if !skipCache { 64 cached, ok := p.bc.GetDid(handle) 65 if ok { 66 return cached, nil 67 } ··· 72 return "", err 73 } 74 75 p.bc.PutDid(handle, did) 76 77 return did, nil 78 } 79 80 func (p *Passport) BustDoc(ctx context.Context, did string) error { 81 return p.bc.BustDoc(did) 82 } 83 84 func (p *Passport) BustDid(ctx context.Context, handle string) error { 85 return p.bc.BustDid(handle) 86 }
··· 19 type Passport struct { 20 h *http.Client 21 bc BackingCache 22 + mu sync.RWMutex 23 } 24 25 func NewPassport(h *http.Client, bc BackingCache) *Passport { ··· 30 return &Passport{ 31 h: h, 32 bc: bc, 33 } 34 } 35 ··· 37 skipCache, _ := ctx.Value("skip-cache").(bool) 38 39 if !skipCache { 40 + p.mu.RLock() 41 cached, ok := p.bc.GetDoc(did) 42 + p.mu.RUnlock() 43 + 44 if ok { 45 return cached, nil 46 } 47 } 48 49 + // TODO: should coalesce requests here 50 doc, err := FetchDidDoc(ctx, p.h, did) 51 if err != nil { 52 return nil, err 53 } 54 55 + p.mu.Lock() 56 p.bc.PutDoc(did, doc) 57 + p.mu.Unlock() 58 59 return doc, nil 60 } ··· 63 skipCache, _ := ctx.Value("skip-cache").(bool) 64 65 if !skipCache { 66 + p.mu.RLock() 67 cached, ok := p.bc.GetDid(handle) 68 + p.mu.RUnlock() 69 + 70 if ok { 71 return cached, nil 72 } ··· 77 return "", err 78 } 79 80 + p.mu.Lock() 81 p.bc.PutDid(handle, did) 82 + p.mu.Unlock() 83 84 return did, nil 85 } 86 87 func (p *Passport) BustDoc(ctx context.Context, did string) error { 88 + p.mu.Lock() 89 + defer p.mu.Unlock() 90 return p.bc.BustDoc(did) 91 } 92 93 func (p *Passport) BustDid(ctx context.Context, handle string) error { 94 + p.mu.Lock() 95 + defer p.mu.Unlock() 96 return p.bc.BustDid(handle) 97 }