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.

10 properties
Schema

Properties

PropertyTypeModeDescriptionRequired
userUser
stored

Reference to the User requesting access

Required
permissionPermission
stored

Reference to the Permission being evaluated

Required
tenantTenant
stored

Reference to the Tenant context for this access decision

Optional
decisionstring
stored

Final access decision

Values: allow, deny

Example: "allow"

Required
strategyAccessDecisionStrategy
stored

Strategy used to combine voter decisions

Required
voterResultsjson
stored

Individual voter votes and their rationale

Example: [{"voter":"permission-voter","vote":"allow","reason":"User has users.read permission"},{"voter":"tenant-membership-voter","vote":"allow","reason":"User is active member of tenant"},{"voter":"business-hours-voter","vote":"abstain","reason":"Not configured for this entity"}]

Required
evaluatedAtdatetime
stored

Date/time when this access decision was made

Example: "2024-11-22T10:30:00Z"

Required
requestContextjson
stored

Context information about the access request

Example: {"ipAddress":"192.168.1.100","userAgent":"Mozilla/5.0...","requestPath":"/api/users","method":"GET"}

Optional
reasonstring
stored

Human-readable explanation of why access was allowed or denied

Example: "Access granted: User has required permission and is active tenant member"

Optional
metadatajson
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"
}