lens @ 6531b1c0a720a63ee3127f9a09f14ba861838b01

  1package ext
  2
  3import (
  4	"context"
  5	"encoding/base64"
  6	"errors"
  7	"net/http"
  8	"time"
  9
 10	"github.com/sirupsen/logrus"
 11
 12	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
 13	"git.sr.ht/~gabrielgio/img/pkg/service"
 14)
 15
 16func HTML(next http.HandlerFunc) http.HandlerFunc {
 17	return func(w http.ResponseWriter, r *http.Request) {
 18		w.Header().Set("Content-Type", "text/html")
 19		next(w, r)
 20	}
 21}
 22
 23type LogMiddleware struct {
 24	entry *logrus.Entry
 25}
 26
 27func NewLogMiddleare(log *logrus.Entry) *LogMiddleware {
 28	return &LogMiddleware{
 29		entry: log,
 30	}
 31}
 32
 33func (l *LogMiddleware) HTTP(next http.HandlerFunc) http.HandlerFunc {
 34	return func(w http.ResponseWriter, r *http.Request) {
 35		start := time.Now()
 36		next(w, r)
 37		elapsed := time.Since(start)
 38		l.entry.
 39			WithField("time", elapsed).
 40			WithField("path", r.URL.Path).
 41			Info(r.Method)
 42	}
 43}
 44
 45type AuthMiddleware struct {
 46	key   []byte
 47	entry *logrus.Entry
 48}
 49
 50func NewAuthMiddleware(key []byte, log *logrus.Entry) *AuthMiddleware {
 51	return &AuthMiddleware{
 52		key:   key,
 53		entry: log.WithField("context", "auth"),
 54	}
 55}
 56
 57func (a *AuthMiddleware) LoggedIn(next http.HandlerFunc) http.HandlerFunc {
 58	return func(w http.ResponseWriter, r *http.Request) {
 59		path := string(r.URL.Path)
 60		if path == "/login" || path == "/initial" {
 61			next(w, r)
 62			return
 63		}
 64
 65		redirectLogin := "/login?redirect=" + path
 66		authBase64, err := r.Cookie("auth")
 67		if errors.Is(err, http.ErrNoCookie) {
 68			a.entry.Info("No auth provided")
 69			http.Redirect(w, r, redirectLogin, http.StatusTemporaryRedirect)
 70			return
 71		}
 72
 73		auth, err := base64.StdEncoding.DecodeString(authBase64.Value)
 74		if err != nil {
 75			a.entry.Error(err)
 76			return
 77		}
 78
 79		token, err := service.ReadToken(auth, a.key)
 80		if err != nil {
 81			a.entry.Error(err)
 82			http.Redirect(w, r, redirectLogin, http.StatusTemporaryRedirect)
 83			return
 84		}
 85		r = r.WithContext(context.WithValue(r.Context(), "token", token))
 86		a.entry.
 87			WithField("userID", token.UserID).
 88			WithField("username", token.Username).
 89			Info("user recognized")
 90		next(w, r)
 91	}
 92}
 93
 94func GetTokenFromCtx(w http.ResponseWriter, r *http.Request) *service.Token {
 95	tokenValue := r.Context().Value("token")
 96	if token, ok := tokenValue.(*service.Token); ok {
 97		return token
 98	}
 99	return nil
100}
101
102type InitialSetupMiddleware struct {
103	userRepository repository.UserRepository
104}
105
106func NewInitialSetupMiddleware(userRepository repository.UserRepository) *InitialSetupMiddleware {
107	return &InitialSetupMiddleware{
108		userRepository: userRepository,
109	}
110}
111
112func (i *InitialSetupMiddleware) Check(next http.HandlerFunc) http.HandlerFunc {
113	return func(w http.ResponseWriter, r *http.Request) {
114		// if user has been set to context it is logged in already
115		token := GetTokenFromCtx(w, r)
116		if token == nil {
117			next(w, r)
118			return
119		}
120
121		path := r.URL.Path
122		if path == "/initial" {
123			next(w, r)
124			return
125		}
126
127		exists, err := i.userRepository.Any(r.Context())
128		if err != nil {
129			InternalServerError(w, err)
130			return
131		}
132
133		if exists {
134			next(w, r)
135			return
136		}
137		http.Redirect(w, r, "/initial", http.StatusTemporaryRedirect)
138	}
139}