lens @ b8b6d3037c524575f140650ac243c16df6a98a92

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