Files
Authorization/services/authorize.go
T
admin ae1831e61f feat: standardize field names and add flexible role_id handling for JWT compatibility
- Rename user_id → users_id across all models, handlers, services, and tests
- Add custom RoleIDs type supporting string/int/array unmarshaling (e.g., "1", 1, [1])
- Implement flexible JSON unmarshaling for JWT Claims to handle field name variants
  - Support both user_id/users_id and email/email_address field names
  - Enable role_id as string ("1"), int (1), or array ([1,2])
- Update AuthorizationContext to handle role_id type flexibility
- Add comprehensive logging to repository, service, and handler layers
  - Entry/exit logs with full context
  - Success (✓) and failure (✗) indicators
  - Step-by-step authorization flow tracking
- Add containsRole helper for multi-role membership checks
- Fix database queries: user_id → users_id, id → permissions_id
- Update all tests to use models.RoleIDs{} syntax
- Change GetRole middleware return type: string → []int
- Maintain backward compatibility with legacy JWT tokens

This change improves integration with external services (MIS) that may send
role_id in different formats and standardizes field naming conventions
throughout the authorization microservice.
2026-02-03 16:35:16 +08:00

103 lines
3.9 KiB
Go

package services
import (
"authorization/models"
"authorization/repository"
"fmt"
"log"
"time"
)
// Authorize performs RBAC + ABAC authorization check
func Authorize(ctx *models.AuthorizationContext) (*models.AuthorizationResult, error) {
startTime := time.Now()
log.Printf("[AuthZ Step 0] Fetching user details for userID=%s", ctx.UsersID)
user, err := repository.GetUserByID(ctx.UsersID)
if err != nil {
log.Printf("✗ User not found for userID=%s: %v", ctx.UsersID, err)
return &models.AuthorizationResult{
Allowed: false,
Message: fmt.Sprintf("User not found: %v", err),
}, nil
}
log.Printf("[AuthZ Step 0] User found: role_id=%d", user.RoleID)
log.Printf("[AuthZ Step 1] Checking if role_id=%d has permission for resource=%s, action=%s", user.RoleID, ctx.Resource, ctx.Action)
permission, err := repository.GetPermissionByResourceActionAndRole(ctx.Resource, ctx.Action, user.RoleID)
if err != nil {
log.Printf("✗ Permission not found or not granted to role_id=%d for resource=%s, action=%s: %v", user.RoleID, ctx.Resource, ctx.Action, err)
return &models.AuthorizationResult{
Allowed: false,
Message: fmt.Sprintf("Permission not granted to your role: %v", err),
}, nil
}
log.Printf("[AuthZ Step 1] Permission found: ID=%d, Name=%s", permission.ID, permission.PermissionName)
// Step 2: Get user attributes
log.Printf("[AuthZ Step 2] Fetching user attributes for userID=%s", ctx.UsersID)
userAttrs, err := repository.GetUserAttributes(ctx.UsersID)
if err != nil {
log.Printf("✗ Failed to get user attributes for userID=%s: %v", ctx.UsersID, err)
return &models.AuthorizationResult{
Allowed: false,
Message: fmt.Sprintf("Failed to get user attributes: %v", err),
}, err
}
ctx.UserAttributes = userAttrs
fmt.Printf("[DEBUG] User attributes map: %+v\n", userAttrs)
fmt.Println("[DEBUG] About to print user attributes")
for k, v := range userAttrs {
log.Printf("User Attribute - %s: %v", k, v)
fmt.Printf("[DEBUG] User Attribute - %s: %v\n", k, v)
}
fmt.Println("[DEBUG] Finished printing user attributes")
log.Printf("[AuthZ Step 2] User attributes retrieved: %d attributes", len(userAttrs))
// Step 3: Get policy attributes for the permission
log.Printf("[AuthZ Step 3] Fetching policy attributes for permissionID=%d", permission.ID)
policies, err := repository.GetPolicyAttributesByPermission(permission.ID)
if err != nil {
log.Printf("✗ Failed to get policies for permissionID=%d: %v", permission.ID, err)
return &models.AuthorizationResult{
Allowed: false,
Message: fmt.Sprintf("Failed to get policies: %v", err),
}, err
}
log.Printf("[AuthZ Step 3] Policies retrieved: %d policies to evaluate", len(policies))
if len(policies) > 0 {
for i, p := range policies {
log.Printf("[DEBUG] Policy %d: AttributeType=%s, AttributeName=%s, Comparison=%s, AttributeValue=%s", i+1, p.AttributeType, p.AttributeName, p.Comparison, p.AttributeValue)
}
} else {
log.Printf("[DEBUG] No policies loaded for permissionID=%d", permission.ID)
}
log.Printf("[AuthZ Step 4] Using RoleID: %s (from context or user record)", ctx.RoleID)
allowed, reason := EvaluatePolicies(policies, ctx)
result := &models.AuthorizationResult{
Allowed: allowed,
}
if allowed {
result.RedirectRoute = "dashboard"
result.Message = "Access granted"
log.Printf("✓ Authorization GRANTED for user=%s, resource=%s, action=%s (evaluated in %v)",
ctx.UsersID, ctx.Resource, ctx.Action, time.Since(startTime))
} else {
log.Printf("✗ Authorization DENIED for user=%s, resource=%s, action=%s - Reason: %s (evaluated in %v)",
ctx.UsersID, ctx.Resource, ctx.Action, reason, time.Since(startTime))
result.Message = reason
}
// Log evaluation time for performance monitoring
evalTime := time.Since(startTime)
if evalTime > 100*time.Millisecond {
fmt.Printf("WARN: Slow authorization evaluation: %v for user=%s, resource=%s, action=%s\n",
evalTime, ctx.UsersID, ctx.Resource, ctx.Action)
}
return result, nil
}