AccessDecision
Represents the result of an access control evaluation, capturing the final authorization decision (allow or deny) along with the rationale, contributing voter votes, and audit trail. This entity serves as an audit log for authorization decisions, enabling security analysis, compliance reporting, debugging access control issues, and understanding why access was granted or denied. It records which voters participated, how each voted (allow, deny, abstain), which strategy was used to combine votes, the final decision, and contextual information about the request (user, entity, action, tenant, IP address, timestamp). The entity supports security monitoring by tracking authorization patterns, failed access attempts, privilege escalation detection, and anomalous access behavior. It serves as the forensic evidence layer for access control, enabling security incident investigation, compliance audits (SOC2, ISO 27001), and access review processes.
Properties
| Property | Type | Mode | Description | Required |
|---|---|---|---|---|
| user | User | stored | Reference to the User requesting access | Required |
| permission | Permission | stored | Reference to the Permission being evaluated | Required |
| tenant | Tenant | stored | Reference to the Tenant context for this access decision | Optional |
| decision | string | stored | Final access decision Values: Example: | Required |
| strategy | AccessDecisionStrategy | stored | Strategy used to combine voter decisions | Required |
| voterResults | json | stored | Individual voter votes and their rationale Example: | Required |
| evaluatedAt | datetime | stored | Date/time when this access decision was made Example: | Required |
| requestContext | json | stored | Context information about the access request Example: | Optional |
| reason | string | stored | Human-readable explanation of why access was allowed or denied Example: | Optional |
| metadata | json | stored | Additional decision metadata | Optional |
Examples
Example 1
{
"@type": "AccessDecision",
"user": {
"@type": "User",
"username": "john.doe"
},
"permission": {
"@type": "Permission",
"entity": {
"@type": "KernelModel",
"name": "User"
},
"action": {
"@type": "PermissionAction",
"name": "read"
}
},
"tenant": {
"@type": "Tenant",
"slug": "acme-corp"
},
"decision": "allow",
"strategy": {
"@type": "AccessDecisionStrategy",
"name": "affirmative",
"label": "Affirmative"
},
"voterResults": [
{
"voter": "permission-voter",
"vote": "allow",
"reason": "User has active users.read permission"
},
{
"voter": "tenant-membership-voter",
"vote": "allow",
"reason": "User is active member"
}
],
"evaluatedAt": "2024-11-22T10:30:00Z",
"requestContext": {
"ipAddress": "192.168.1.100",
"requestPath": "/api/users"
},
"reason": "Access granted: User has required permission and is active tenant member"
}Example 2
{
"@type": "AccessDecision",
"user": {
"@type": "User",
"username": "jane.smith"
},
"permission": {
"@type": "Permission",
"entity": {
"@type": "KernelModel",
"name": "SystemConfig"
},
"action": {
"@type": "PermissionAction",
"name": "update"
}
},
"tenant": {
"@type": "Tenant",
"slug": "acme-corp"
},
"decision": "deny",
"strategy": {
"@type": "AccessDecisionStrategy",
"name": "unanimous",
"label": "Unanimous"
},
"voterResults": [
{
"voter": "permission-voter",
"vote": "allow",
"reason": "User has system-config.update permission"
},
{
"voter": "ip-whitelist-voter",
"vote": "deny",
"reason": "IP 203.0.113.50 not in whitelist"
},
{
"voter": "business-hours-voter",
"vote": "deny",
"reason": "Access attempted at 23:45 (outside 09:00-17:00)"
}
],
"evaluatedAt": "2024-11-22T23:45:00Z",
"requestContext": {
"ipAddress": "203.0.113.50",
"requestPath": "/api/system/config"
},
"reason": "Access denied: Outside business hours and IP not whitelisted (unanimous strategy requires all voters to allow)"
}Example 3
{
"@type": "AccessDecision",
"user": {
"@type": "User",
"username": "bob.wilson"
},
"permission": {
"@type": "Permission",
"entity": {
"@type": "KernelModel",
"name": "Invoice"
},
"entityId": "invoice_12345",
"action": {
"@type": "PermissionAction",
"name": "approve"
}
},
"tenant": {
"@type": "Tenant",
"slug": "acme-corp"
},
"decision": "allow",
"strategy": {
"@type": "AccessDecisionStrategy",
"name": "affirmative",
"label": "Affirmative"
},
"voterResults": [
{
"voter": "permission-voter",
"vote": "abstain",
"reason": "No entity-level permission found"
},
{
"voter": "ownership-voter",
"vote": "allow",
"reason": "User created this invoice"
},
{
"voter": "custom-approval-voter",
"vote": "allow",
"reason": "Invoice amount $500 below approval threshold"
}
],
"evaluatedAt": "2024-11-22T14:20:00Z",
"requestContext": {
"ipAddress": "192.168.1.105",
"requestPath": "/api/invoices/invoice_12345/approve"
},
"reason": "Access granted: User owns the invoice and amount is below threshold"
}