fixed multiple roles in 1 policy
This commit is contained in:
@@ -3,17 +3,16 @@ package services
|
||||
import (
|
||||
"authorization/models"
|
||||
"authorization/repository"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Authorize performs RBAC + ABAC authorization check
|
||||
func Authorize(repo *repository.PermissionRepository, ctx *models.AuthorizationContext) (*models.AuthorizationResult, error) {
|
||||
func Authorize(ctx *models.AuthorizationContext) (*models.AuthorizationResult, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Step 1: Find the permission for the requested resource and action
|
||||
permission, err := repo.GetPermissionByResourceAndAction(ctx.Resource, ctx.Action)
|
||||
permission, err := repository.GetPermissionByResourceAndAction(ctx.Resource, ctx.Action)
|
||||
if err != nil {
|
||||
return &models.AuthorizationResult{
|
||||
Allowed: false,
|
||||
@@ -22,7 +21,7 @@ func Authorize(repo *repository.PermissionRepository, ctx *models.AuthorizationC
|
||||
}
|
||||
|
||||
// Step 2: Get user attributes
|
||||
userAttrs, err := repo.GetUserAttributes(ctx.UserID)
|
||||
userAttrs, err := repository.GetUserAttributes(ctx.UserID)
|
||||
if err != nil {
|
||||
return &models.AuthorizationResult{
|
||||
Allowed: false,
|
||||
@@ -32,7 +31,7 @@ func Authorize(repo *repository.PermissionRepository, ctx *models.AuthorizationC
|
||||
ctx.UserAttributes = userAttrs
|
||||
|
||||
// Step 3: Get policy attributes for the permission
|
||||
policies, err := repo.GetPolicyAttributesByPermission(permission.ID)
|
||||
policies, err := repository.GetPolicyAttributesByPermission(permission.ID)
|
||||
if err != nil {
|
||||
return &models.AuthorizationResult{
|
||||
Allowed: false,
|
||||
@@ -64,9 +63,7 @@ func Authorize(repo *repository.PermissionRepository, ctx *models.AuthorizationC
|
||||
}
|
||||
|
||||
// CheckPermission is a simplified authorization check
|
||||
func CheckPermission(db *sql.DB, userID, resource, action string, resourceData map[string]string) (bool, string, error) {
|
||||
repo := repository.NewPermissionRepository(db)
|
||||
|
||||
func CheckPermission(userID, resource, action string, resourceData map[string]string) (bool, string, error) {
|
||||
ctx := &models.AuthorizationContext{
|
||||
UserID: userID,
|
||||
Resource: resource,
|
||||
@@ -78,7 +75,7 @@ func CheckPermission(db *sql.DB, userID, resource, action string, resourceData m
|
||||
// Add current time to environment
|
||||
ctx.Environment["time"] = time.Now().Format(time.RFC3339)
|
||||
|
||||
result, err := Authorize(repo, ctx)
|
||||
result, err := Authorize(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("Authorization error: %v", err), err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package services
|
||||
import (
|
||||
"authorization/models"
|
||||
"authorization/repository"
|
||||
"database/sql"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -21,8 +21,7 @@ func getCachedUserAttributes(s *models.CachedAuthorizationService, userID string
|
||||
}
|
||||
|
||||
// Cache miss - fetch from DB
|
||||
repo := s.Repo.(*repository.PermissionRepository)
|
||||
attrs, err := repo.GetUserAttributes(userID)
|
||||
attrs, err := repository.GetUserAttributes(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -37,15 +36,14 @@ func getCachedUserAttributes(s *models.CachedAuthorizationService, userID string
|
||||
|
||||
// refreshCache reloads permissions and policies from database
|
||||
func refreshCache(s *models.CachedAuthorizationService) {
|
||||
repo := s.Repo.(*repository.PermissionRepository)
|
||||
// Load all permissions
|
||||
permissions, err := repo.GetAllPermissions()
|
||||
permissions, err := repository.GetAllPermissions()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Load all policies
|
||||
policies, err := repo.GetAllPolicyAttributes()
|
||||
policies, err := repository.GetAllPolicyAttributes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -90,15 +88,14 @@ func cacheRefreshLoop(s *models.CachedAuthorizationService) {
|
||||
}
|
||||
}
|
||||
|
||||
func NewCachedAuthorizationService(db *sql.DB) *models.CachedAuthorizationService {
|
||||
func NewCachedAuthorizationService() *models.CachedAuthorizationService {
|
||||
service := &models.CachedAuthorizationService{
|
||||
Repo: repository.NewPermissionRepository(db),
|
||||
PermissionCache: make(map[string]*models.Permission),
|
||||
PolicyCache: make(map[int][]models.PolicyAttribute),
|
||||
UserAttrCache: make(map[string]map[string]string),
|
||||
CacheMutex: &sync.RWMutex{},
|
||||
UserAttrMutex: &sync.RWMutex{},
|
||||
CacheExpiry: 5 * time.Minute,
|
||||
CacheExpiry: 30 * time.Second, // Changed from 5 minutes for faster updates
|
||||
LastCacheRefresh: time.Now(),
|
||||
}
|
||||
|
||||
@@ -115,13 +112,14 @@ func NewCachedAuthorizationService(db *sql.DB) *models.CachedAuthorizationServic
|
||||
func AuthorizeWithCache(s *models.CachedAuthorizationService, ctx *models.AuthorizationContext) (*models.AuthorizationResult, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Step 1: Get permission from cache
|
||||
// Step 1: Get permission from cache()
|
||||
cacheKey := ctx.Resource + ":" + ctx.Action
|
||||
cacheMutex := s.CacheMutex.(*sync.RWMutex)
|
||||
cacheMutex.RLock()
|
||||
permission, exists := s.PermissionCache[cacheKey]
|
||||
cacheMutex.RUnlock()
|
||||
|
||||
log.Print("Cached authorization lookup for user=", ctx.UserID, ", resource=", ctx.Resource, ", action=", ctx.Action)
|
||||
if !exists {
|
||||
return &models.AuthorizationResult{
|
||||
Allowed: false,
|
||||
@@ -159,8 +157,15 @@ func AuthorizeWithCache(s *models.CachedAuthorizationService, ctx *models.Author
|
||||
|
||||
// Performance monitoring
|
||||
evalTime := time.Since(startTime)
|
||||
|
||||
if evalTime < 50*time.Millisecond {
|
||||
log.Print("Cached authorization evaluation time: ", evalTime,
|
||||
" for user=", ctx.UserID, ", resource=", ctx.Resource, ", action=", ctx.Action)
|
||||
}
|
||||
|
||||
if evalTime > 50*time.Millisecond {
|
||||
// Cached should be much faster
|
||||
log.Print("WARN: Slow cached authorization evaluation: ", evalTime,
|
||||
" for user=", ctx.UserID, ", resource=", ctx.Resource, ", action=", ctx.Action)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
@@ -3,14 +3,13 @@ package services
|
||||
import (
|
||||
"authorization/models"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// resolveVariables resolves variable references like ${resource.region}
|
||||
func resolveVariables(value string, ctx *models.AuthorizationContext) string {
|
||||
// Pattern: ${type.attribute}
|
||||
re := regexp.MustCompile(`\$\{([^.]+)\.([^}]+)\}`)
|
||||
|
||||
return re.ReplaceAllStringFunc(value, func(match string) string {
|
||||
@@ -41,7 +40,6 @@ func resolveVariables(value string, ctx *models.AuthorizationContext) string {
|
||||
})
|
||||
}
|
||||
|
||||
// compare performs the actual comparison based on operator
|
||||
func compare(actual, expected, operator string) bool {
|
||||
actual = strings.TrimSpace(actual)
|
||||
expected = strings.TrimSpace(expected)
|
||||
@@ -82,7 +80,6 @@ func compare(actual, expected, operator string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// numericCompare performs numeric comparison
|
||||
func numericCompare(actual, expected string, compareFn func(float64, float64) bool) bool {
|
||||
actualNum, err1 := strconv.ParseFloat(actual, 64)
|
||||
expectedNum, err2 := strconv.ParseFloat(expected, 64)
|
||||
@@ -94,11 +91,13 @@ func numericCompare(actual, expected string, compareFn func(float64, float64) bo
|
||||
return compareFn(actualNum, expectedNum)
|
||||
}
|
||||
|
||||
// inComparison checks if actual value is in comma-separated list
|
||||
func inComparison(actual, expected string) bool {
|
||||
values := strings.Split(expected, ",")
|
||||
actual = strings.ToLower(strings.TrimSpace(actual))
|
||||
|
||||
log.Print("IN comparison values: ", values)
|
||||
log.Print("Actual value: ", actual)
|
||||
log.Print("Expected values: ", expected)
|
||||
for _, val := range values {
|
||||
if strings.ToLower(strings.TrimSpace(val)) == actual {
|
||||
return true
|
||||
@@ -108,62 +107,62 @@ func inComparison(actual, expected string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// evaluatePolicy evaluates a single policy attribute
|
||||
func evaluatePolicy(
|
||||
policy models.PolicyAttribute,
|
||||
ctx *models.AuthorizationContext,
|
||||
) (bool, string) {
|
||||
// Get the actual value based on attribute type
|
||||
func evaluatePolicy(policyAttribute models.PolicyAttribute, ctx *models.AuthorizationContext) (bool, string) {
|
||||
var actualValue string
|
||||
var exists bool
|
||||
|
||||
switch policy.AttributeType {
|
||||
case "user":
|
||||
actualValue, exists = ctx.UserAttributes[policy.AttributeName]
|
||||
if !exists {
|
||||
return false, fmt.Sprintf("User attribute '%s' not found", policy.AttributeName)
|
||||
}
|
||||
log.Print("Attribute Type: ", policyAttribute.AttributeType)
|
||||
|
||||
case "resource":
|
||||
actualValue, exists = ctx.ResourceData[policy.AttributeName]
|
||||
switch policyAttribute.AttributeType {
|
||||
case "user":
|
||||
log.Print("Fetching from User Attributes")
|
||||
log.Print("User Attributes: ", ctx.UserAttributes)
|
||||
actualValue, exists = ctx.UserAttributes[policyAttribute.AttributeName]
|
||||
if !exists {
|
||||
return false, fmt.Sprintf("Resource attribute '%s' not found", policy.AttributeName)
|
||||
return false, fmt.Sprintf("User attribute '%s' not found", policyAttribute.AttributeName)
|
||||
}
|
||||
log.Print("Found User Attribute: ", actualValue)
|
||||
case "resource":
|
||||
actualValue, exists = ctx.ResourceData[policyAttribute.AttributeName]
|
||||
if !exists {
|
||||
return false, fmt.Sprintf("Resource attribute '%s' not found", policyAttribute.AttributeName)
|
||||
}
|
||||
|
||||
case "environment":
|
||||
actualValue, exists = ctx.Environment[policy.AttributeName]
|
||||
actualValue, exists = ctx.Environment[policyAttribute.AttributeName]
|
||||
if !exists {
|
||||
return false, fmt.Sprintf("Environment attribute '%s' not found", policy.AttributeName)
|
||||
return false, fmt.Sprintf("Environment attribute '%s' not found", policyAttribute.AttributeName)
|
||||
}
|
||||
|
||||
default:
|
||||
return false, fmt.Sprintf("Unknown attribute type: %s", policy.AttributeType)
|
||||
return false, fmt.Sprintf("Unknown attribute type: %s", policyAttribute.AttributeType)
|
||||
}
|
||||
|
||||
// Handle variable substitution (e.g., ${resource.region})
|
||||
expectedValue := resolveVariables(policy.AttributeValue, ctx)
|
||||
expectedValue := resolveVariables(policyAttribute.AttributeValue, ctx)
|
||||
|
||||
// Perform comparison
|
||||
satisfied := compare(actualValue, expectedValue, policy.Comparison)
|
||||
fmt.Printf("[POLICY EVALUATION] Type: %s, Attribute: %s\n", policyAttribute.AttributeType, policyAttribute.AttributeName)
|
||||
fmt.Printf(" Expected: %s %s %s\n", policyAttribute.AttributeName, policyAttribute.Comparison, expectedValue)
|
||||
fmt.Printf(" Actual: %s = %s\n", policyAttribute.AttributeName, actualValue)
|
||||
|
||||
log.Print("Comparison: ", policyAttribute.Comparison)
|
||||
satisfied := compare(actualValue, expectedValue, policyAttribute.Comparison)
|
||||
|
||||
if !satisfied {
|
||||
fmt.Printf(" Result: ❌ FAILED\n\n")
|
||||
return false, fmt.Sprintf(
|
||||
"Policy failed: %s %s %s (actual: %s)",
|
||||
policy.AttributeName,
|
||||
policy.Comparison,
|
||||
policyAttribute.AttributeName,
|
||||
policyAttribute.Comparison,
|
||||
expectedValue,
|
||||
actualValue,
|
||||
)
|
||||
}
|
||||
|
||||
fmt.Printf(" Result: ✓ PASSED\n\n")
|
||||
return true, ""
|
||||
}
|
||||
|
||||
// EvaluatePolicies checks if all policy attributes are satisfied
|
||||
func EvaluatePolicies(
|
||||
policies []models.PolicyAttribute,
|
||||
ctx *models.AuthorizationContext,
|
||||
) (bool, string) {
|
||||
func EvaluatePolicies(policies []models.PolicyAttribute, ctx *models.AuthorizationContext) (bool, string) {
|
||||
if len(policies) == 0 {
|
||||
// No policies means permission is granted by default (RBAC only)
|
||||
return true, "No policies to evaluate"
|
||||
|
||||
Reference in New Issue
Block a user