main / internal/interface/web/middleware.go
2.6 KB · Go Raw
1package web
2
3import (
4 "context"
5 "net/http"
6 "strconv"
7
8 "github.com/alexedwards/scs/v2"
9 "github.com/justinas/nosurf"
10
11 "gitgud/internal/app"
12 "gitgud/internal/domain"
13 "gitgud/internal/infra/git"
14 "gitgud/internal/interface/web/templates"
15)
16
17const sessionUserIDKey = "user_id"
18
19type ctxKey int
20
21const userCtxKey ctxKey = iota
22
23type Handlers struct {
24 users *app.UserService
25 repos *app.RepoService
26 browse *app.BrowseService
27 issues *app.IssueService
28 pulls *app.PullService
29 gitAccess *app.GitAccessService
30 gitBackend *git.Backend
31 sm *scs.SessionManager
32}
33
34func NewHandlers(users *app.UserService, repos *app.RepoService, browse *app.BrowseService, issues *app.IssueService, pulls *app.PullService, gitAccess *app.GitAccessService, gitBackend *git.Backend, sm *scs.SessionManager) *Handlers {
35 return &Handlers{users: users, repos: repos, browse: browse, issues: issues, pulls: pulls, gitAccess: gitAccess, gitBackend: gitBackend, sm: sm}
36}
37
38func (h *Handlers) withUser(next http.Handler) http.Handler {
39 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
40 if id := h.sm.GetInt64(r.Context(), sessionUserIDKey); id != 0 {
41 if u, err := h.users.ByID(r.Context(), strconv.FormatInt(id, 10)); err == nil {
42 r = r.WithContext(context.WithValue(r.Context(), userCtxKey, u))
43 }
44 }
45 next.ServeHTTP(w, r)
46 })
47}
48
49func (h *Handlers) requireAuth(next http.Handler) http.Handler {
50 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
51 if currentUser(r.Context()) == nil {
52 http.Redirect(w, r, "/login", http.StatusSeeOther)
53 return
54 }
55 next.ServeHTTP(w, r)
56 })
57}
58
59func currentUser(ctx context.Context) *domain.User {
60 u, _ := ctx.Value(userCtxKey).(*domain.User)
61 return u
62}
63
64func (h *Handlers) withFlash(next http.Handler) http.Handler {
65 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66 if msg := h.sm.PopString(r.Context(), "flash"); msg != "" {
67 r = r.WithContext(templates.WithFlash(r.Context(), msg))
68 }
69 next.ServeHTTP(w, r)
70 })
71}
72
73func (h *Handlers) flash(r *http.Request, msg string) {
74 h.sm.Put(r.Context(), "flash", msg)
75}
76
77func csrf(next http.Handler) http.Handler {
78 s := nosurf.New(injectCSRFToken(next))
79 s.ExemptRegexp(`/(git-upload-pack|git-receive-pack)$`)
80 s.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
81 http.Error(w, "CSRF token invalid", http.StatusBadRequest)
82 }))
83 return s
84}
85
86func injectCSRFToken(next http.Handler) http.Handler {
87 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88 next.ServeHTTP(w, r.WithContext(templates.WithCSRF(r.Context(), nosurf.Token(r))))
89 })
90}