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}