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) return default: api.internalServerError(w, r, err) return } } ctx = context.WithValue(ctx, postCtx, post) next.ServeHTTP(w, r.WithContext(ctx)) }) } 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) }) } 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) protectAdminRoutes(requiredRole string, next http.HandlerFunc) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := getUserFromContext(r) 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) 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 }