fixed region fetching in user_attributes

This commit is contained in:
2026-04-16 13:42:50 +08:00
parent f0bc603a5f
commit 29cf10c379
7 changed files with 43 additions and 54 deletions
+4 -3
View File
@@ -3,7 +3,10 @@ module authorization
go 1.25.1 go 1.25.1
require ( require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/alicebob/miniredis/v2 v2.35.0
github.com/getsentry/sentry-go v0.39.0 github.com/getsentry/sentry-go v0.39.0
github.com/go-redis/redismock/v9 v9.2.0
github.com/go-sql-driver/mysql v1.9.3 github.com/go-sql-driver/mysql v1.9.3
github.com/golang-jwt/jwt/v5 v5.3.0 github.com/golang-jwt/jwt/v5 v5.3.0
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
@@ -17,17 +20,15 @@ require (
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect
github.com/alicebob/miniredis/v2 v2.35.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cespares/response v0.0.0-20260416052801-b22a14921d12 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.6 // indirect github.com/go-openapi/spec v0.20.6 // indirect
github.com/go-openapi/swag v0.19.15 // indirect github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-redis/redismock/v9 v9.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect github.com/mailru/easyjson v0.7.6 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+12
View File
@@ -14,12 +14,16 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespares/response v0.0.0-20260416052801-b22a14921d12 h1:VtwZEFXJADUIpECuGittyHNLRAkEOr2HdCcticfz6Ow=
github.com/cespares/response v0.0.0-20260416052801-b22a14921d12/go.mod h1:+9XxGj6565PWnCxvMvC0QCrg90beAzkK5jOHNNPNG3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getsentry/sentry-go v0.39.0 h1:uhnexj8PNCyCve37GSqxXOeXHh4cJNLNNB4w70Jtgo0= github.com/getsentry/sentry-go v0.39.0 h1:uhnexj8PNCyCve37GSqxXOeXHh4cJNLNNB4w70Jtgo0=
github.com/getsentry/sentry-go v0.39.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= github.com/getsentry/sentry-go v0.39.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
@@ -67,6 +71,12 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -129,6 +139,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+14 -13
View File
@@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"authorization/helper"
"authorization/middleware" "authorization/middleware"
"authorization/models" "authorization/models"
"authorization/services" "authorization/services"
@@ -9,6 +8,8 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
sabat "github.com/cespares/response"
) )
var authService *models.CachedAuthorizationService var authService *models.CachedAuthorizationService
@@ -36,7 +37,7 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
claims, ok := middleware.GetClaims(r) claims, ok := middleware.GetClaims(r)
if !ok { if !ok {
log.Printf("ERROR: Missing JWT claims in request context (method=%s, path=%s)", r.Method, r.URL.Path) log.Printf("ERROR: Missing JWT claims in request context (method=%s, path=%s)", r.Method, r.URL.Path)
helper.RespondWithError(w, http.StatusUnauthorized, "Unauthorized") sabat.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return return
} }
@@ -48,7 +49,7 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := io.ReadAll(r.Body) bodyBytes, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
log.Printf("ERROR: Failed to read authorization request body: %v", err) log.Printf("ERROR: Failed to read authorization request body: %v", err)
helper.RespondWithError(w, http.StatusBadRequest, "Invalid request body") sabat.RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return return
} }
log.Printf("Raw authorization request body: %s", string(bodyBytes)) log.Printf("Raw authorization request body: %s", string(bodyBytes))
@@ -56,7 +57,7 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
// Decode JSON into AuthorizationContext // Decode JSON into AuthorizationContext
if err := json.Unmarshal(bodyBytes, &ctx); err != nil { if err := json.Unmarshal(bodyBytes, &ctx); err != nil {
log.Printf("ERROR: Failed to unmarshal request body: %v", err) log.Printf("ERROR: Failed to unmarshal request body: %v", err)
helper.RespondWithError(w, http.StatusBadRequest, "Invalid request payload") sabat.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
return return
} }
@@ -65,7 +66,7 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("User ID ctx=%s, resource=%s, action=%s, roleID=%d", ctx.UsersID, ctx.Resource, ctx.Action, ctx.RoleID) log.Printf("User ID ctx=%s, resource=%s, action=%s, roleID=%d", ctx.UsersID, ctx.Resource, ctx.Action, ctx.RoleID)
if ctx.UsersID == "" || ctx.Resource == "" || ctx.Action == "" { if ctx.UsersID == "" || ctx.Resource == "" || ctx.Action == "" {
log.Printf("ERROR: Missing required fields - UsersID=%s, Resource=%s, Action=%s", ctx.UsersID, ctx.Resource, ctx.Action) log.Printf("ERROR: Missing required fields - UsersID=%s, Resource=%s, Action=%s", ctx.UsersID, ctx.Resource, ctx.Action)
helper.RespondWithError(w, http.StatusBadRequest, "Missing required fields: users_id, resource, action") sabat.RespondWithError(w, http.StatusBadRequest, "Missing required fields: users_id, resource, action")
return return
} }
@@ -74,7 +75,7 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
// Verify JWT user matches request user (security check) // Verify JWT user matches request user (security check)
if ctx.UsersID != claims.UsersID { if ctx.UsersID != claims.UsersID {
log.Printf("ERROR: User ID mismatch - ctx.UsersID='%s' vs claims.UsersID='%s'", ctx.UsersID, claims.UsersID) log.Printf("ERROR: User ID mismatch - ctx.UsersID='%s' vs claims.UsersID='%s'", ctx.UsersID, claims.UsersID)
helper.RespondWithError(w, http.StatusForbidden, "User ID mismatch") sabat.RespondWithError(w, http.StatusForbidden, "User ID mismatch")
return return
} }
@@ -107,7 +108,7 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("[Handler] Role candidate resolution - requested=%v, finalCandidates=%v", requestedRoles, validRoles) log.Printf("[Handler] Role candidate resolution - requested=%v, finalCandidates=%v", requestedRoles, validRoles)
if len(validRoles) == 0 { if len(validRoles) == 0 {
log.Printf("ERROR: Role mismatch for user=%s - requestedRoles=%v, claimRoles=%v", ctx.UsersID, requestedRoles, claimRoles) log.Printf("ERROR: Role mismatch for user=%s - requestedRoles=%v, claimRoles=%v", ctx.UsersID, requestedRoles, claimRoles)
helper.RespondWithError(w, http.StatusForbidden, "Role ID mismatch") sabat.RespondWithError(w, http.StatusForbidden, "Role ID mismatch")
return return
} }
@@ -119,29 +120,29 @@ func AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("[Handler] Performing authorization check for user=%s, resource=%s, action=%s", ctx.UsersID, ctx.Resource, ctx.Action) log.Printf("[Handler] Performing authorization check for user=%s, resource=%s, action=%s", ctx.UsersID, ctx.Resource, ctx.Action)
result, err := services.AuthorizeWithCache(authService, &ctx) result, err := services.AuthorizeWithCache(authService, &ctx)
if err != nil { if err != nil {
helper.LogError(err, "Authorization service error") sabat.LogError(err, "Authorization service error")
log.Printf("✗ Authorization service error for user=%s: %v", ctx.UsersID, err) log.Printf("✗ Authorization service error for user=%s: %v", ctx.UsersID, err)
helper.RespondWithError(w, http.StatusInternalServerError, "Authorization check failed") sabat.RespondWithError(w, http.StatusInternalServerError, "Authorization check failed")
return return
} }
// Return result // Return result
if result.Allowed { if result.Allowed {
log.Printf("✓ [Handler] Authorization ALLOWED - Returning 200 OK to client") log.Printf("✓ [Handler] Authorization ALLOWED - Returning 200 OK to client")
// Return response matching AuthorizationResponse model for client compatibility // Return sabat matching Authorizationsabat model for client compatibility
response := map[string]interface{}{ response := map[string]interface{}{
"allowed": result.Allowed, "allowed": result.Allowed,
"reason": result.Message, "reason": result.Message,
} }
helper.RespondWithJSON(w, http.StatusOK, response) sabat.RespondWithJSON(w, http.StatusOK, response)
} else { } else {
log.Printf("✗ [Handler] Authorization DENIED - Returning 403 Forbidden to client (reason: %s)", result.Message) log.Printf("✗ [Handler] Authorization DENIED - Returning 403 Forbidden to client (reason: %s)", result.Message)
// Return response matching AuthorizationResponse model for client compatibility // Return sabat matching Authorizationsabat model for client compatibility
response := map[string]interface{}{ response := map[string]interface{}{
"allowed": result.Allowed, "allowed": result.Allowed,
"reason": result.Message, "reason": result.Message,
} }
helper.RespondWithJSON(w, http.StatusForbidden, response) sabat.RespondWithJSON(w, http.StatusForbidden, response)
} }
} }
+4 -3
View File
@@ -2,13 +2,14 @@ package handlers
import ( import (
"authorization/db" "authorization/db"
"authorization/helper"
"authorization/models" "authorization/models"
"authorization/redisclient" "authorization/redisclient"
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"time" "time"
sabat "github.com/cespares/response"
) )
// HealthHandler provides a basic liveness check // HealthHandler provides a basic liveness check
@@ -22,7 +23,7 @@ func HealthHandler(w http.ResponseWriter, r *http.Request) {
response := models.HealthResponse{ response := models.HealthResponse{
Status: "ok", Status: "ok",
} }
helper.RespondWithJSON(w, http.StatusOK, response) sabat.RespondWithJSON(w, http.StatusOK, response)
} }
// ReadyHandler checks if the service is ready to handle requests // ReadyHandler checks if the service is ready to handle requests
@@ -81,6 +82,6 @@ func ReadyHandler(w http.ResponseWriter, r *http.Request) {
Status: status, Status: status,
Services: services, Services: services,
}); err != nil { }); err != nil {
helper.LogError(err, "Error encoding health response") sabat.LogError(err, "Error encoding health response")
} }
} }
-28
View File
@@ -1,28 +0,0 @@
package helper
import (
"encoding/json"
"net/http"
)
func RespondWithError(w http.ResponseWriter, statusCode int, message string) {
w.Header().Set(ContentTypeHeader, ApplicationJSON)
w.WriteHeader(statusCode)
if encodeErr := json.NewEncoder(w).Encode(map[string]string{ErrorLabel: message}); encodeErr != nil {
LogError(encodeErr, ErrorEncodingResponse)
}
}
func RespondWithMessage(w http.ResponseWriter, message string) {
if encodeErr := json.NewEncoder(w).Encode(map[string]string{MessageLabel: message}); encodeErr != nil {
LogError(encodeErr, ErrorEncodingResponse)
}
}
func RespondWithJSON(w http.ResponseWriter, statusCode int, data interface{}) {
w.Header().Set(ContentTypeHeader, ApplicationJSON)
w.WriteHeader(statusCode)
if encodeErr := json.NewEncoder(w).Encode(data); encodeErr != nil {
LogError(encodeErr, ErrorEncodingResponse)
}
}
+4 -3
View File
@@ -1,7 +1,6 @@
package middleware package middleware
import ( import (
"authorization/helper"
"authorization/models" "authorization/models"
"authorization/redisclient" "authorization/redisclient"
"context" "context"
@@ -16,6 +15,8 @@ import (
"sync" "sync"
"time" "time"
sabat "github.com/cespares/response"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
) )
@@ -208,7 +209,7 @@ func JWTAuth(next http.HandlerFunc) http.HandlerFunc {
// Extract token from header // Extract token from header
tokenString, ok := extractBearerToken(r.Header.Get("Authorization")) tokenString, ok := extractBearerToken(r.Header.Get("Authorization"))
if !ok { if !ok {
helper.RespondWithError(w, http.StatusUnauthorized, "Unauthorized") sabat.RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return return
} }
@@ -223,7 +224,7 @@ func JWTAuth(next http.HandlerFunc) http.HandlerFunc {
// Parse and validate token // Parse and validate token
claims, err := parseAndValidateToken(tokenString) claims, err := parseAndValidateToken(tokenString)
if err != nil { if err != nil {
helper.RespondWithError(w, http.StatusUnauthorized, errExpiredToken) sabat.RespondWithError(w, http.StatusUnauthorized, errExpiredToken)
return return
} }
+5 -4
View File
@@ -1,13 +1,14 @@
package middleware package middleware
import ( import (
"authorization/helper"
"authorization/models" "authorization/models"
"authorization/redisclient" "authorization/redisclient"
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
sabat "github.com/cespares/response"
) )
// DefaultRateLimitConfig returns default rate limiting settings // DefaultRateLimitConfig returns default rate limiting settings
@@ -24,7 +25,7 @@ func RateLimiterMiddleware(config models.RateLimitConfig) func(http.HandlerFunc)
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
// Fail-open: Skip rate limiting if Redis is not available (prevents full outage) // Fail-open: Skip rate limiting if Redis is not available (prevents full outage)
if redisclient.RDB == nil { if redisclient.RDB == nil {
helper.LogError(nil, "Rate limiter: Redis not available, allowing request (fail-open)") sabat.LogError(nil, "Rate limiter: Redis not available, allowing request (fail-open)")
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
@@ -41,13 +42,13 @@ func RateLimiterMiddleware(config models.RateLimitConfig) func(http.HandlerFunc)
allowed, err := checkRateLimit(identifier, config) allowed, err := checkRateLimit(identifier, config)
if err != nil { if err != nil {
// On error, fail open (allow request) but log the error // On error, fail open (allow request) but log the error
helper.LogError(err, "rate limiter error") sabat.LogError(err, "rate limiter error")
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
if !allowed { if !allowed {
helper.RespondWithError(w, http.StatusTooManyRequests, "Rate limit exceeded") sabat.RespondWithError(w, http.StatusTooManyRequests, "Rate limit exceeded")
return return
} }