ikurotime / gitgud

public
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}