feat: initial commit

This commit is contained in:
2025-03-31 16:17:35 +01:00
commit 6719f8a61a
91 changed files with 7119 additions and 0 deletions
+208
View File
@@ -0,0 +1,208 @@
package main
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/FernandoVideira/LK_API_Temp/internal/store"
"github.com/go-chi/chi/v5"
"github.com/golang-jwt/jwt/v5"
)
func (api *api) BasicAuthMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//* read the auth header
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
api.unauthorizedBasicError(w, r, fmt.Errorf("missing Authorization header"))
return
}
//* parse the header -> get the base64
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || parts[0] != "Basic" {
api.unauthorizedBasicError(w, r, fmt.Errorf("invalid Authorization header"))
return
}
//* decode the base64
decoded, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
api.unauthorizedBasicError(w, r, err)
return
}
username := api.config.auth.basic.username
pwd := api.config.auth.basic.password
//* check the credentials
creds := strings.SplitN(string(decoded), ":", 2)
if len(creds) != 2 || creds[0] != username || creds[1] != pwd {
api.unauthorizedBasicError(w, r, fmt.Errorf("invalid credentials"))
return
}
next.ServeHTTP(w, r)
})
}
}
func (api *api) AuthTokenMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
api.unauthorizedError(w, r, fmt.Errorf("missing Authorization header"))
return
}
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
api.unauthorizedError(w, r, fmt.Errorf("invalid Authorization header"))
return
}
token := parts[1]
jwtToken, err := api.authenticator.ValidateToken(token)
if err != nil {
api.unauthorizedError(w, r, err)
return
}
claims := jwtToken.Claims.(jwt.MapClaims)
userID, err := strconv.ParseInt(fmt.Sprintf("%.f", claims["sub"]), 10, 64)
if err != nil {
api.unauthorizedError(w, r, fmt.Errorf("invalid token"))
return
}
ctx := r.Context()
user, err := api.getUser(ctx, userID)
if err != nil {
api.unauthorizedError(w, r, err)
return
}
ctx = context.WithValue(ctx, userContextKey, user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (api *api) postsContextMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
idParam := chi.URLParam(r, "id")
id, err := strconv.ParseInt(idParam, 10, 64)
if err != nil {
api.badRequestError(w, r, err)
return
}
ctx := r.Context()
post, err := api.store.Posts.GetByID(ctx, id)
if err != nil {
switch {
case errors.Is(err, store.ErrNotFound):
api.notFoundError(w, r, err)
default:
api.internalServerError(w, r, err)
}
return
}
ctx = context.WithValue(ctx, postCtx, post)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (api *api) checkPostOwnership(requiredRole string, next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := getUserFromContext(r)
post := getPostFromContext(r)
if user.ID == post.UserID {
next.ServeHTTP(w, r)
return
}
allowed, err := api.checkRolePrecedence(r.Context(), user, requiredRole)
if err != nil {
api.internalServerError(w, r, err)
return
}
if !allowed {
api.forbiddenError(w, r, fmt.Errorf("forbidden"))
return
}
next.ServeHTTP(w, r)
})
}
func (api *api) checkProfileOwnership(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := getUserFromContext(r)
profile := getUserFromContext(r)
if user.ID == profile.ID {
next.ServeHTTP(w, r)
return
}
api.forbiddenError(w, r, fmt.Errorf("forbidden"))
})
}
func (api *api) checkRolePrecedence(ctx context.Context, user *store.User, roleName string) (bool, error) {
role, err := api.store.Roles.GetByName(ctx, roleName)
if err != nil {
return false, err
}
return user.Role.Level >= role.Level, nil
}
func (api *api) getUser(ctx context.Context, id int64) (*store.User, error) {
if !api.config.redisCfg.enabled {
return api.store.Users.GetByID(ctx, id)
}
user, err := api.cacheStore.Users.Get(ctx, id)
if err != nil {
return nil, err
}
if user == nil {
user, err = api.store.Users.GetByID(ctx, id)
if err != nil {
return nil, err
}
if err := api.cacheStore.Users.Set(ctx, user); err != nil {
return nil, err
}
}
return user, nil
}
func (api *api) RateLimiterMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if api.config.rateLimiter.Enabled {
if allow, retryAfter := api.rateLimiter.Allow(r.RemoteAddr); !allow {
api.rateLimitExceededError(w, r, retryAfter.String())
return
}
}
next.ServeHTTP(w, r)
})
}