feat: initial commit
This commit is contained in:
@@ -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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user