fixed ABAC
This commit is contained in:
+14
-21
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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,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)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user