An atproto PDS written in Go
at main 2.6 kB view raw
1package helpers 2 3import ( 4 crand "crypto/rand" 5 "encoding/hex" 6 "errors" 7 "math/rand" 8 "net/url" 9 10 "github.com/Azure/go-autorest/autorest/to" 11 "github.com/labstack/echo/v4" 12 "github.com/lestrrat-go/jwx/v2/jwk" 13) 14 15// This will confirm to the regex in the application if 5 chars are used for each side of the - 16// /^[A-Z2-7]{5}-[A-Z2-7]{5}$/ 17var letters = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") 18 19func InputError(e echo.Context, custom *string) error { 20 msg := "InvalidRequest" 21 if custom != nil { 22 msg = *custom 23 } 24 return genericError(e, 400, msg) 25} 26 27func ServerError(e echo.Context, suffix *string) error { 28 msg := "Internal server error" 29 if suffix != nil { 30 msg += ". " + *suffix 31 } 32 return genericError(e, 400, msg) 33} 34 35func UnauthorizedError(e echo.Context, suffix *string) error { 36 msg := "Unauthorized" 37 if suffix != nil { 38 msg += ". " + *suffix 39 } 40 return genericError(e, 401, msg) 41} 42 43func ForbiddenError(e echo.Context, suffix *string) error { 44 msg := "Forbidden" 45 if suffix != nil { 46 msg += ". " + *suffix 47 } 48 return genericError(e, 403, msg) 49} 50 51func InvalidTokenError(e echo.Context) error { 52 return InputError(e, to.StringPtr("InvalidToken")) 53} 54 55func ExpiredTokenError(e echo.Context) error { 56 // WARN: See https://github.com/bluesky-social/atproto/discussions/3319 57 return e.JSON(400, map[string]string{ 58 "error": "ExpiredToken", 59 "message": "*", 60 }) 61} 62 63func genericError(e echo.Context, code int, msg string) error { 64 return e.JSON(code, map[string]string{ 65 "error": msg, 66 }) 67} 68 69func RandomVarchar(length int) string { 70 b := make([]rune, length) 71 for i := range b { 72 b[i] = letters[rand.Intn(len(letters))] 73 } 74 return string(b) 75} 76 77func RandomHex(n int) (string, error) { 78 bytes := make([]byte, n) 79 if _, err := crand.Read(bytes); err != nil { 80 return "", err 81 } 82 return hex.EncodeToString(bytes), nil 83} 84 85func RandomBytes(n int) []byte { 86 bs := make([]byte, n) 87 crand.Read(bs) 88 return bs 89} 90 91func ParseJWKFromBytes(b []byte) (jwk.Key, error) { 92 return jwk.ParseKey(b) 93} 94 95func OauthParseHtu(htu string) (string, error) { 96 u, err := url.Parse(htu) 97 if err != nil { 98 return "", errors.New("`htu` is not a valid URL") 99 } 100 101 if u.User != nil { 102 _, containsPass := u.User.Password() 103 if u.User.Username() != "" || containsPass { 104 return "", errors.New("`htu` must not contain credentials") 105 } 106 } 107 108 if u.Scheme != "http" && u.Scheme != "https" { 109 return "", errors.New("`htu` must be http or https") 110 } 111 112 return OauthNormalizeHtu(u), nil 113} 114 115func OauthNormalizeHtu(u *url.URL) string { 116 return u.Scheme + "://" + u.Host + u.RawPath 117}