• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

charmbracelet / charm / 8113356253

01 Mar 2024 02:57PM UTC coverage: 14.408% (-6.2%) from 20.65%
8113356253

Pull #246

github

web-flow
chore(deps): bump actions/setup-go from 4 to 5

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #246: chore(deps): bump actions/setup-go from 4 to 5

876 of 6080 relevant lines covered (14.41%)

0.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

43.01
/server/middleware.go
1
package server
2

3
import (
4
        "context"
5
        "crypto"
6
        "fmt"
7
        "net/http"
8
        "strings"
9

10
        "github.com/charmbracelet/log"
11

12
        jwtmiddleware "github.com/auth0/go-jwt-middleware/v2"
13
        "github.com/auth0/go-jwt-middleware/v2/validator"
14
        charm "github.com/charmbracelet/charm/proto"
15
)
16

17
type contextKey string
18

19
var (
20
        ctxUserKey   contextKey = "charmUser"
21
        ctxPublicKey contextKey = "public"
22
)
23

24
// MaxFSRequestSize is the maximum size of a request body for fs endpoints.
25
var MaxFSRequestSize int64 = 1024 * 1024 * 1024 // 1GB
26

27
// RequestLimitMiddleware limits the request body size to the specified limit.
28
func RequestLimitMiddleware() func(http.Handler) http.Handler {
2✔
29
        return func(h http.Handler) http.Handler {
4✔
30
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2✔
31
                        var maxRequestSize int64
×
32
                        if strings.HasPrefix(r.URL.Path, "/v1/fs") {
×
33
                                maxRequestSize = MaxFSRequestSize
×
34
                        } else {
×
35
                                maxRequestSize = 1024 * 1024 // limit request size to 1MB for other endpoints
×
36
                        }
×
37
                        // Check if the request body is too large using Content-Length
38
                        if r.ContentLength > maxRequestSize {
×
39
                                http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
×
40
                                return
×
41
                        }
×
42
                        // Limit body read using MaxBytesReader
43
                        r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
×
44
                        h.ServeHTTP(w, r)
×
45
                })
46
        }
47
}
48

49
// PublicPrefixesMiddleware allows for the specification of non-authed URL
50
// prefixes. These won't be checked for JWT bearers or Charm user accounts.
51
func PublicPrefixesMiddleware(prefixes []string) func(http.Handler) http.Handler {
2✔
52
        return func(next http.Handler) http.Handler {
10✔
53
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
9✔
54
                        public := false
1✔
55
                        for _, p := range prefixes {
3✔
56
                                if strings.HasPrefix(r.URL.Path, p) {
2✔
57
                                        public = true
×
58
                                }
×
59
                        }
60
                        ctx := context.WithValue(r.Context(), ctxPublicKey, public)
1✔
61
                        next.ServeHTTP(w, r.WithContext(ctx))
1✔
62
                })
63
        }
64
}
65

66
// JWTMiddleware creates a new middleware function that will validate JWT
67
// tokens based on the supplied public key.
68
func JWTMiddleware(pk crypto.PublicKey, iss string, aud []string) (func(http.Handler) http.Handler, error) {
2✔
69
        jm, err := jwtMiddlewareImpl(pk, iss, aud)
2✔
70
        if err != nil {
2✔
71
                return nil, err
×
72
        }
×
73
        return func(next http.Handler) http.Handler {
8✔
74
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
7✔
75
                        if isPublic(r) {
1✔
76
                                next.ServeHTTP(w, r)
×
77
                        } else {
1✔
78
                                jm(next).ServeHTTP(w, r)
1✔
79
                        }
1✔
80
                })
81
        }, nil
82
}
83

84
// CharmUserMiddleware looks up and authenticates a Charm user based on the
85
// provided JWT in the request.
86
func CharmUserMiddleware(s *HTTPServer) func(http.Handler) http.Handler {
2✔
87
        return func(h http.Handler) http.Handler {
6✔
88
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
4✔
89
                        if isPublic(r) {
×
90
                                h.ServeHTTP(w, r)
×
91
                        } else {
×
92
                                id, err := charmIDFromRequest(r)
×
93
                                if err != nil {
×
94
                                        log.Error("cannot get charm id from request", "err", err)
×
95
                                        s.renderError(w)
×
96
                                        return
×
97
                                }
×
98
                                u, err := s.db.GetUserWithID(id)
×
99
                                if err == charm.ErrMissingUser {
×
100
                                        s.renderCustomError(w, fmt.Sprintf("missing user for id '%s'", id), http.StatusNotFound)
×
101
                                        return
×
102
                                } else if err != nil {
×
103
                                        log.Error("cannot read request body", "err", err)
×
104
                                        s.renderError(w)
×
105
                                        return
×
106
                                }
×
107
                                ctx := context.WithValue(r.Context(), ctxUserKey, u)
×
108
                                h.ServeHTTP(w, r.WithContext(ctx))
×
109
                        }
110
                })
111
        }
112
}
113

114
func isPublic(r *http.Request) bool {
1✔
115
        public, ok := r.Context().Value(ctxPublicKey).(bool)
1✔
116
        if !ok {
1✔
117
                log.Debug("cannot get public value from context")
×
118
                return false
×
119
        }
×
120

121
        return public
1✔
122
}
123

124
func charmIDFromRequest(r *http.Request) (string, error) {
×
125
        claims := r.Context().Value(jwtmiddleware.ContextKey{})
×
126
        if claims == "" {
×
127
                return "", fmt.Errorf("missing jwt claims key in context")
×
128
        }
×
129
        cl := claims.(*validator.ValidatedClaims).RegisteredClaims
×
130
        sub := cl.Subject
×
131
        if sub == "" {
×
132
                return "", fmt.Errorf("missing subject key in claims map")
×
133
        }
×
134
        return sub, nil
×
135
}
136

137
func jwtMiddlewareImpl(pk crypto.PublicKey, iss string, aud []string) (func(http.Handler) http.Handler, error) {
2✔
138
        kf := func(ctx context.Context) (interface{}, error) {
3✔
139
                return pk, nil
1✔
140
        }
1✔
141
        v, err := validator.New(
2✔
142
                kf,
2✔
143
                validator.EdDSA,
2✔
144
                iss,
2✔
145
                aud,
2✔
146
        )
2✔
147
        if err != nil {
2✔
148
                return nil, err
×
149
        }
×
150
        mw := jwtmiddleware.New(v.ValidateToken)
2✔
151
        return mw.CheckJWT, nil
2✔
152
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc