fixed ABAC

This commit is contained in:
2026-01-29 11:43:22 +08:00
parent 3abaf6f592
commit d5606b34f9
5 changed files with 45 additions and 168 deletions
+14 -21
View File
@@ -45,6 +45,13 @@ func Authorize(ctx *models.AuthorizationContext) (*models.AuthorizationResult, e
}, err }, err
} }
ctx.UserAttributes = userAttrs 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)) log.Printf("[AuthZ Step 2] User attributes retrieved: %d attributes", len(userAttrs))
// Step 3: Get policy attributes for the permission // Step 3: Get policy attributes for the permission
@@ -58,6 +65,13 @@ func Authorize(ctx *models.AuthorizationContext) (*models.AuthorizationResult, e
}, err }, err
} }
log.Printf("[AuthZ Step 3] Policies retrieved: %d policies to evaluate", len(policies)) 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) log.Printf("[AuthZ Step 4] Using RoleID: %s (from context or user record)", ctx.RoleID)
allowed, reason := EvaluatePolicies(policies, ctx) allowed, reason := EvaluatePolicies(policies, ctx)
@@ -86,24 +100,3 @@ func Authorize(ctx *models.AuthorizationContext) (*models.AuthorizationResult, e
return result, nil return result, nil
} }
// CheckPermission is a simplified authorization check
func CheckPermission(userID, resource, action string, resourceData map[string]string) (bool, string, error) {
ctx := &models.AuthorizationContext{
UserID: userID,
Resource: resource,
Action: action,
ResourceData: resourceData,
Environment: make(map[string]string),
}
// Add current time to environment
ctx.Environment["time"] = time.Now().Format(time.RFC3339)
result, err := Authorize(ctx)
if err != nil {
return false, fmt.Sprintf("Authorization error: %v", err), err
}
return result.Allowed, result.Message, nil
}
-137
View File
@@ -230,140 +230,3 @@ func TestAuthorize_PolicyAttributesError(t *testing.T) {
t.Error("Expected access denied") t.Error("Expected access denied")
} }
} }
func TestCheckPermission_Success(t *testing.T) {
mock, cleanup := setupMockDB(t)
defer cleanup()
// Mock user query
userRows := sqlmock.NewRows([]string{"user_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
"emp_id", "is_logged_in", "first_logged_in", "home_address", "contact_number", "device_id",
"role_id", "is_deleted", "secret_key", "is_activated", "created_at", "updated_at"}).
AddRow("user123", "John", "", "Doe", "", "john@example.com",
"EMP123", "Y", "Y", "123 Street", "09123456789", "device1",
1, "N", "secret", "Y", time.Now(), time.Now())
mock.ExpectQuery("SELECT user_id, first_name, middle_initial, last_name, suffix, email_address").
WithArgs("user123").
WillReturnRows(userRows)
// Mock permission query with role check
permRows := sqlmock.NewRows([]string{"id", "permission_name", "description", "resource", "action"}).
AddRow(1, "read_document", "Read document permission", "document", "read")
mock.ExpectQuery("SELECT p.id, p.permission_name, p.description, p.resource, p.action FROM permissions p INNER JOIN role_permissions rp").
WithArgs("document", "read", 1).
WillReturnRows(permRows)
// Mock user attributes query
attrRows := sqlmock.NewRows([]string{"attribute_name", "attribute_value"}).
AddRow("department", "engineering")
mock.ExpectQuery("SELECT attribute_name, attribute_value FROM user_attributes WHERE user_id = \\?").
WithArgs("user123").
WillReturnRows(attrRows)
// Mock policy attributes query
policyRows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"})
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes WHERE permission_id = \\?").
WithArgs(1).
WillReturnRows(policyRows)
resourceData := map[string]string{"document_id": "123"}
allowed, message, err := CheckPermission("user123", "document", "read", resourceData)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if !allowed {
t.Error("Expected access allowed")
}
if message != "Access granted" {
t.Errorf("Expected 'Access granted', got '%s'", message)
}
}
func TestCheckPermission_Denied(t *testing.T) {
mock, cleanup := setupMockDB(t)
defer cleanup()
// Mock user query
userRows := sqlmock.NewRows([]string{"user_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
"emp_id", "is_logged_in", "first_logged_in", "home_address", "contact_number", "device_id",
"role_id", "is_deleted", "secret_key", "is_activated", "created_at", "updated_at"}).
AddRow("user123", "John", "", "Doe", "", "john@example.com",
"EMP123", "Y", "Y", "123 Street", "09123456789", "device1",
1, "N", "secret", "Y", time.Now(), time.Now())
mock.ExpectQuery("SELECT user_id, first_name, middle_initial, last_name, suffix, email_address").
WithArgs("user123").
WillReturnRows(userRows)
// Mock permission query with role check - should fail
mock.ExpectQuery("SELECT p.id, p.permission_name, p.description, p.resource, p.action FROM permissions p INNER JOIN role_permissions rp").
WithArgs("document", "read", 1).
WillReturnError(errors.New("permission not found"))
resourceData := map[string]string{"document_id": "123"}
allowed, message, err := CheckPermission("user123", "document", "read", resourceData)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if allowed {
t.Error("Expected access denied")
}
if message == "" {
t.Error("Expected error message")
}
}
func TestCheckPermission_NilResourceData(t *testing.T) {
mock, cleanup := setupMockDB(t)
defer cleanup()
// Mock user query
userRows := sqlmock.NewRows([]string{"user_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
"emp_id", "is_logged_in", "first_logged_in", "home_address", "contact_number", "device_id",
"role_id", "is_deleted", "secret_key", "is_activated", "created_at", "updated_at"}).
AddRow("user123", "John", "", "Doe", "", "john@example.com",
"EMP123", "Y", "Y", "123 Street", "09123456789", "device1",
1, "N", "secret", "Y", time.Now(), time.Now())
mock.ExpectQuery("SELECT user_id, first_name, middle_initial, last_name, suffix, email_address").
WithArgs("user123").
WillReturnRows(userRows)
// Mock permission query with role check
permRows := sqlmock.NewRows([]string{"id", "permission_name", "description", "resource", "action"}).
AddRow(1, "read_document", "Read document permission", "document", "read")
mock.ExpectQuery("SELECT p.id, p.permission_name, p.description, p.resource, p.action FROM permissions p INNER JOIN role_permissions rp").
WithArgs("document", "read", 1).
WillReturnRows(permRows)
// Mock user attributes query
attrRows := sqlmock.NewRows([]string{"attribute_name", "attribute_value"})
mock.ExpectQuery("SELECT attribute_name, attribute_value FROM user_attributes WHERE user_id = \\?").
WithArgs("user123").
WillReturnRows(attrRows)
// Mock policy attributes query
policyRows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"})
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes WHERE permission_id = \\?").
WithArgs(1).
WillReturnRows(policyRows)
allowed, message, err := CheckPermission("user123", "document", "read", nil)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
// Should not panic with nil resourceData
if !allowed {
t.Logf("Access denied with message: %s", message)
}
}
+16 -2
View File
@@ -16,7 +16,7 @@ const (
permissionCachePrefix = "authz:perm:" permissionCachePrefix = "authz:perm:"
policyCachePrefix = "authz:policy:" policyCachePrefix = "authz:policy:"
userAttrCachePrefix = "authz:userattr:" userAttrCachePrefix = "authz:userattr:"
cacheTTL = 30 * time.Second cacheTTL = 5 * time.Second
) )
// getCachedUserAttributes retrieves user attributes from Redis or DB // getCachedUserAttributes retrieves user attributes from Redis or DB
@@ -213,7 +213,7 @@ func NewCachedAuthorizationService() *models.CachedAuthorizationService {
UserAttrCache: make(map[string]map[string]string), UserAttrCache: make(map[string]map[string]string),
CacheMutex: &sync.RWMutex{}, CacheMutex: &sync.RWMutex{},
UserAttrMutex: &sync.RWMutex{}, UserAttrMutex: &sync.RWMutex{},
CacheExpiry: 30 * time.Second, // Changed from 5 minutes for faster updates CacheExpiry: 5 * time.Second, // Changed from 5 minutes for faster updates
LastCacheRefresh: time.Now(), LastCacheRefresh: time.Now(),
} }
@@ -280,12 +280,26 @@ func AuthorizeWithCache(s *models.CachedAuthorizationService, ctx *models.Author
}, err }, err
} }
ctx.UserAttributes = userAttrs ctx.UserAttributes = userAttrs
fmt.Printf("[DEBUG] User attributes map: %+v\n", userAttrs)
fmt.Println("[DEBUG] About to print user attributes (cache)")
for k, v := range userAttrs {
log.Printf("User Attribute (cache) - %s: %v", k, v)
fmt.Printf("[DEBUG] User Attribute (cache) - %s: %v\n", k, v)
}
fmt.Println("[DEBUG] Finished printing user attributes (cache)")
log.Printf("[AuthZ Step 2] User attributes retrieved: %d attributes", len(userAttrs)) log.Printf("[AuthZ Step 2] User attributes retrieved: %d attributes", len(userAttrs))
// Step 3: Get policies from distributed cache // Step 3: Get policies from distributed cache
log.Printf("[AuthZ Step 3] Fetching policies for permissionID=%d", permission.ID) log.Printf("[AuthZ Step 3] Fetching policies for permissionID=%d", permission.ID)
policies := getPoliciesFromCache(s, permission.ID) policies := getPoliciesFromCache(s, permission.ID)
log.Printf("[AuthZ Step 3] Policies retrieved: %d policies to evaluate", len(policies)) 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) log.Printf("[AuthZ Step 4] Using RoleID: %s (from context or user record)", ctx.RoleID)
allowed, reason := EvaluatePolicies(policies, ctx) allowed, reason := EvaluatePolicies(policies, ctx)
+1 -1
View File
@@ -58,7 +58,7 @@ func TestNewCachedAuthorizationService(t *testing.T) {
if service.UserAttrCache == nil { if service.UserAttrCache == nil {
t.Error("Expected UserAttrCache to be initialized") t.Error("Expected UserAttrCache to be initialized")
} }
if service.CacheExpiry != 30*time.Second { if service.CacheExpiry != 5*time.Second {
t.Errorf("Expected CacheExpiry 30s, got %v", service.CacheExpiry) t.Errorf("Expected CacheExpiry 30s, got %v", service.CacheExpiry)
} }
+14 -7
View File
@@ -142,27 +142,34 @@ func evaluatePolicy(policyAttribute models.PolicyAttribute, ctx *models.Authoriz
return true, "" return true, ""
} }
// Always check user attributes in context if type is user
switch policyAttribute.AttributeType { switch policyAttribute.AttributeType {
case "user": case "user":
log.Print("Fetching from User Attributes") log.Print("Checking user attribute in context for: ", policyAttribute.AttributeName)
log.Print("User Attributes: ", ctx.UserAttributes) if ctx.UserAttributes == nil {
return false, "User attributes missing in context"
}
actualValue, exists = ctx.UserAttributes[policyAttribute.AttributeName] actualValue, exists = ctx.UserAttributes[policyAttribute.AttributeName]
if !exists { if !exists {
return false, fmt.Sprintf("User attribute '%s' not found", policyAttribute.AttributeName) return false, fmt.Sprintf("User attribute '%s' not found in context", policyAttribute.AttributeName)
} }
log.Print("Found User Attribute: ", actualValue) log.Print("Found User Attribute: ", actualValue)
case "resource": case "resource":
if ctx.ResourceData == nil {
return false, "Resource data missing in context"
}
actualValue, exists = ctx.ResourceData[policyAttribute.AttributeName] actualValue, exists = ctx.ResourceData[policyAttribute.AttributeName]
if !exists { if !exists {
return false, fmt.Sprintf("Resource attribute '%s' not found", policyAttribute.AttributeName) return false, fmt.Sprintf("Resource attribute '%s' not found in context", policyAttribute.AttributeName)
} }
case "environment": case "environment":
if ctx.Environment == nil {
return false, "Environment data missing in context"
}
actualValue, exists = ctx.Environment[policyAttribute.AttributeName] actualValue, exists = ctx.Environment[policyAttribute.AttributeName]
if !exists { if !exists {
return false, fmt.Sprintf("Environment attribute '%s' not found", policyAttribute.AttributeName) return false, fmt.Sprintf("Environment attribute '%s' not found in context", policyAttribute.AttributeName)
} }
default: default:
return false, fmt.Sprintf("Unknown attribute type: %s", policyAttribute.AttributeType) return false, fmt.Sprintf("Unknown attribute type: %s", policyAttribute.AttributeType)
} }