chore: sync submodule state (parent ref update)
Made-with: Cursor
This commit is contained in:
57
gateway/go/middleware/auth.go
Normal file
57
gateway/go/middleware/auth.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"solacenet-gateway/config"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// AuthMiddleware validates JWT tokens
|
||||
func AuthMiddleware(cfg *config.Config) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "Authorization header required",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Extract token
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "Invalid authorization header format",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := parts[1]
|
||||
|
||||
// Parse and validate token
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(cfg.JWTSecret), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "Invalid token",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Extract claims
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
c.Set("userID", claims["sub"])
|
||||
c.Set("tenantID", claims["tenantId"])
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
152
gateway/go/middleware/capability-check.go
Normal file
152
gateway/go/middleware/capability-check.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"solacenet-gateway/cache"
|
||||
"solacenet-gateway/config"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type PolicyDecisionRequest struct {
|
||||
TenantID string `json:"tenantId"`
|
||||
ProgramID string `json:"programId,omitempty"`
|
||||
CapabilityID string `json:"capabilityId"`
|
||||
Region string `json:"region,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Actor string `json:"actor,omitempty"`
|
||||
Context map[string]interface{} `json:"context,omitempty"`
|
||||
}
|
||||
|
||||
type PolicyDecisionResponse struct {
|
||||
Allowed bool `json:"allowed"`
|
||||
Mode string `json:"mode"`
|
||||
Limits map[string]interface{} `json:"limits,omitempty"`
|
||||
ReasonCode string `json:"reasonCode,omitempty"`
|
||||
DecisionID string `json:"decisionId"`
|
||||
}
|
||||
|
||||
// CapabilityCheckMiddleware checks if a capability is enabled before routing
|
||||
func CapabilityCheckMiddleware(cfg *config.Config, cache *cache.Cache) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Extract capability ID from request path or header
|
||||
capabilityID := c.GetHeader("X-Capability-ID")
|
||||
if capabilityID == "" {
|
||||
// Try to extract from path pattern
|
||||
// This is a simplified version - adjust based on your routing
|
||||
capabilityID = extractCapabilityFromPath(c.Request.URL.Path)
|
||||
}
|
||||
|
||||
if capabilityID == "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// Extract context from request
|
||||
tenantID := c.GetHeader("X-Tenant-ID")
|
||||
programID := c.GetHeader("X-Program-ID")
|
||||
region := c.GetHeader("X-Region")
|
||||
channel := c.GetHeader("X-Channel")
|
||||
actor := c.GetHeader("X-Actor")
|
||||
|
||||
// Check cache first
|
||||
cacheKey := fmt.Sprintf("policy:decision:%s:%s:%s:%s:%s:%s",
|
||||
tenantID, programID, capabilityID, region, channel, actor)
|
||||
|
||||
if cached, err := cache.Get(cacheKey); err == nil && cached != nil {
|
||||
var decision PolicyDecisionResponse
|
||||
if json.Unmarshal(cached, &decision) == nil {
|
||||
if !decision.Allowed {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Capability not available",
|
||||
"reasonCode": decision.ReasonCode,
|
||||
"mode": decision.Mode,
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set("policyDecision", decision)
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Call policy engine
|
||||
decisionReq := PolicyDecisionRequest{
|
||||
TenantID: tenantID,
|
||||
ProgramID: programID,
|
||||
CapabilityID: capabilityID,
|
||||
Region: region,
|
||||
Channel: channel,
|
||||
Actor: actor,
|
||||
}
|
||||
|
||||
decision, err := callPolicyEngine(cfg, decisionReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to check capability",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Cache the decision
|
||||
if decisionJSON, err := json.Marshal(decision); err == nil {
|
||||
cache.Set(cacheKey, decisionJSON, time.Duration(cfg.CacheTTL)*time.Second)
|
||||
}
|
||||
|
||||
if !decision.Allowed {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Capability not available",
|
||||
"reasonCode": decision.ReasonCode,
|
||||
"mode": decision.Mode,
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("policyDecision", decision)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func callPolicyEngine(cfg *config.Config, req PolicyDecisionRequest) (*PolicyDecisionResponse, error) {
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Post(
|
||||
fmt.Sprintf("%s/api/v1/solacenet/policy/decide", cfg.PolicyEngineURL),
|
||||
"application/json",
|
||||
bytes.NewBuffer(reqBody),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var decision PolicyDecisionResponse
|
||||
if err := json.Unmarshal(body, &decision); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &decision, nil
|
||||
}
|
||||
|
||||
func extractCapabilityFromPath(path string) string {
|
||||
// Simplified extraction - adjust based on your routing patterns
|
||||
// Example: /api/v1/payments/... -> "payment-gateway"
|
||||
// This should be configured based on your actual routing
|
||||
return ""
|
||||
}
|
||||
14
gateway/go/middleware/rate-limit.go
Normal file
14
gateway/go/middleware/rate-limit.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RateLimitMiddleware implements rate limiting
|
||||
// In production, use a proper rate limiting library like golang.org/x/time/rate
|
||||
func RateLimitMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Simplified rate limiting - implement proper rate limiting in production
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user