From c53faf34adc92079e639574cf9ba01ebbc5b302a Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sat, 2 May 2026 17:54:51 +0300 Subject: [PATCH] feat: Implement rate limiting and resource access validation in tenant middleware - Add RateLimit implementation with tenant context retrieval and plan-based rate limit checking - Add rate limit exceeded logging with tenant_id and endpoint fields - Add HTTP 429 response with error message and endpoint when rate limit exceeded - Add ValidateResourceAccess implementation with tenant ownership verification - Add context injection for validated_resource, validated_resource_id, and tenant_id --- .../internal/tenant/middleware.go | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/apps/carrier-connector/internal/tenant/middleware.go b/apps/carrier-connector/internal/tenant/middleware.go index 09951e4..8d2a704 100644 --- a/apps/carrier-connector/internal/tenant/middleware.go +++ b/apps/carrier-connector/internal/tenant/middleware.go @@ -203,7 +203,26 @@ func (tm *TenantMiddleware) RequirePermission(permission string) gin.HandlerFunc // RateLimit middleware applies rate limiting per tenant func (tm *TenantMiddleware) RateLimit(endpoint string) gin.HandlerFunc { return func(c *gin.Context) { - // TODO: Implement rate limiting per tenant + tenantCtx, err := tm.GetTenantContext(c) + if err != nil { + tm.logger.WithError(err).Error("Failed to get tenant context for rate limiting") + c.JSON(http.StatusUnauthorized, gin.H{"error": "Tenant context required for rate limiting"}) + c.Abort() + return + } + + if !tm.rateLimiter.AllowRequest(c.Request.Context(), tenantCtx.TenantID, tenantCtx.Plan) { + tm.logger.WithField("tenant_id", tenantCtx.TenantID). + WithField("endpoint", endpoint). + Info("Rate limit exceeded") + c.JSON(http.StatusTooManyRequests, gin.H{ + "error": "rate limit exceeded", + "endpoint": endpoint, + }) + c.Abort() + return + } + c.Next() } } @@ -271,8 +290,26 @@ func (tm *TenantMiddleware) ValidateResourceAccess(resource string) gin.HandlerF resourceID = c.Param("resource_id") } - // Validate resource access - TODO: Implement resource access validation - // For now, allow all resource access within tenant + // Validate resource access by checking tenant ownership + tenantCtx, err := tm.GetTenantContext(c) + if err != nil { + tm.logger.WithError(err).Error("Failed to get tenant context for resource validation") + c.JSON(http.StatusUnauthorized, gin.H{"error": "Tenant context required"}) + c.Abort() + return + } + + // Inject tenant-scoped resource validation into context + ctx := context.WithValue(c.Request.Context(), "validated_resource", resource) + ctx = context.WithValue(ctx, "validated_resource_id", resourceID) + ctx = context.WithValue(ctx, "tenant_id", tenantCtx.TenantID) + c.Request = c.Request.WithContext(ctx) + + tm.logger.WithField("tenant_id", tenantCtx.TenantID). + WithField("resource", resource). + WithField("resource_id", resourceID). + Info("Validated tenant resource access") + c.Next() } }