initial commit
This commit is contained in:
138
internal/http/middleware.go
Normal file
138
internal/http/middleware.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.yigid.dev/fyb/tingz/internal/user"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
const usernameKey contextKey = "username"
|
||||
|
||||
func AdminAuthMiddleware(adminToken string, logger *slog.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
logger.Warn("missing authorization header")
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]string{
|
||||
"status": "error",
|
||||
"error": "missing authorization header",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
logger.Warn("invalid authorization header format")
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]string{
|
||||
"status": "error",
|
||||
"error": "invalid authorization header format",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
token := parts[1]
|
||||
if subtle.ConstantTimeCompare([]byte(token), []byte(adminToken)) != 1 {
|
||||
logger.Warn("invalid admin token")
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]string{
|
||||
"status": "error",
|
||||
"error": "invalid admin token",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func UserAuthMiddleware(userMgr *user.Manager, logger *slog.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
logger.Warn("missing authorization header")
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]string{
|
||||
"status": "error",
|
||||
"error": "missing authorization header",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
logger.Warn("invalid authorization header format")
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]string{
|
||||
"status": "error",
|
||||
"error": "invalid authorization header format",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
token := parts[1]
|
||||
username, err := userMgr.GetByToken(r.Context(), token)
|
||||
if err != nil {
|
||||
logger.Warn("invalid user token", slog.String("error", err.Error()))
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]string{
|
||||
"status": "error",
|
||||
"error": "invalid token",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), usernameKey, username)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func LoggingMiddleware(logger *slog.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
||||
next.ServeHTTP(wrapped, r)
|
||||
|
||||
duration := time.Since(start)
|
||||
logger.Info("request completed",
|
||||
slog.String("method", r.Method),
|
||||
slog.String("path", r.URL.Path),
|
||||
slog.Int("status", wrapped.statusCode),
|
||||
slog.Duration("duration", duration),
|
||||
slog.String("remote_addr", r.RemoteAddr))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func MaxBytesMiddleware(maxBytes int64) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxBytes)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type responseWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.statusCode = code
|
||||
rw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func getUsernameFromContext(ctx context.Context) (string, bool) {
|
||||
username, ok := ctx.Value(usernameKey).(string)
|
||||
return username, ok
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user