contexa-iam

AI Security Expressions

Extend Spring Security's SpEL expression system with Zero Trust action predicates. Contexa's AbstractAISecurityExpressionRoot exposes the current ZeroTrustAction to both URL-level and method-level authorization decisions.

Overview

Contexa extends Spring Security's expression-based access control with AI-integrated predicates that evaluate the current ZeroTrustAction. These expressions can be used in both URL-level security configurations and method-level annotations.

The expression system is built on two levels:

  • URL-Level - CustomWebSecurityExpressionRoot for HTTP request matching in URL policies
  • Method-Level - CustomMethodSecurityExpressionRoot for annotation-based authorization with @Protectable

Both classes extend AbstractAISecurityExpressionRoot, which provides the shared #ai.* expression API. At runtime, the expression roots resolve the current ZeroTrustAction from request-scoped state and repository-backed lookups; method-level evaluation adds a short-lived Caffeine cache on top of that lookup path.

Expression API Reference

All expressions are accessed through the #ai prefix in SpEL. These are defined in AbstractAISecurityExpressionRoot and available at both URL and method levels.

Expression Returns Description
#ai.isAllowed() boolean Returns true if the AI assessment result is ALLOW
#ai.isBlocked() boolean Returns true if the AI assessment result is BLOCK
#ai.needsChallenge() boolean Returns true if the AI assessment result is CHALLENGE
#ai.needsEscalation() boolean Returns true if the AI assessment result is ESCALATE
#ai.isPendingAnalysis() boolean Returns true if the AI assessment result is PENDING_ANALYSIS
#ai.hasAction(String) boolean Checks whether the current ZeroTrustAction matches a specific action string
#ai.hasActionIn(String...) boolean Returns true if the current action matches any of the provided action strings
#ai.hasActionOrDefault(defaultAction, actions...) boolean Uses defaultAction when no action is currently available, then checks whether the resolved action is included in the provided action list

URL-Level Expressions

CustomWebSecurityExpressionRoot extends AbstractAISecurityExpressionRoot and adds URL-specific helper methods for HTTP request matching. It is used when persisted URL policies are evaluated through Spring Security's web expression layer.

Additional URL-Level Methods

Method Returns Description
hasIpAddress(String ipAddress) boolean Checks if the request originates from the given IP address or CIDR range
getHttpMethod() String Returns the HTTP method of the current request (GET, POST, etc.)

Usage Examples

These expressions are used in stored URL policy conditions managed through the IAM admin surfaces:

SpEL
// Allow only if AI approves AND user has USER role
#ai.isAllowed() and hasRole('USER')

// Allow if AI approves AND request is from internal network
#ai.isAllowed() and hasIpAddress('10.0.0.0/8')

// Deny only if AI explicitly blocks (permissive mode)
!(#ai.isBlocked())

// Allow or challenge actions only
#ai.hasActionIn('ALLOW', 'CHALLENGE')

Method-Level Expressions

CustomMethodSecurityExpressionRoot extends AbstractAISecurityExpressionRoot and adds method-level authorization capabilities including ownership verification and full hasPermission() support.

Additional Method-Level Methods

Method Returns Description
hasPermission(Object, Object) boolean Full permission evaluation with ownership checking via CompositePermissionEvaluator
hasPermission(Object, String, Object) boolean Target-type-based permission evaluation with domain-specific routing

Action Lookup

Method-level expressions resolve ZeroTrustAction through a layered lookup path:

  • Request attribute - if the current HTTP request already carries contexa.zeroTrustAction, that action is used first
  • Caffeine local cache - 5-second TTL, maximum 10,000 entries, method-level only
  • ZeroTrustActionRepository lookup - shared backing lookup used by both URL and method evaluation; the concrete storage depends on deployment configuration

Usage with @Protectable

Method-level AI expressions are typically used with the @Protectable annotation and @PreAuthorize:

Java
@PreAuthorize("#ai.isAllowed() and hasPermission(#id, 'USER', 'READ')")
@Protectable(ownerField = "userId")
public User getUser(Long id) { ... }

@PreAuthorize("#ai.isAllowed() and hasPermission(#orderId, 'ORDER', 'UPDATE')")
@Protectable(ownerField = "customerId", sync = true)
public void updateOrder(Long orderId, OrderDto dto) { ... }

@PreAuthorize("!(#ai.isBlocked()) and hasRole('ADMIN')")
@Protectable
public List<AuditLog> getAuditLogs() { ... }

URL vs Method Comparison

The two expression roots serve different authorization layers and have distinct capabilities:

Feature URL-Level Method-Level
Expression Root CustomWebSecurityExpressionRoot CustomMethodSecurityExpressionRoot
Action Lookup Request attribute + repository lookup Request attribute + Caffeine (5s TTL) + repository lookup
Ownership Check No Yes (via ownerField)
hasPermission() Stripped (URL-level only) Full support
hasIpAddress() Yes No
Trigger HTTP request matching @Protectable annotation
Typical Use Stored URL policies Service method annotations

ZeroTrustAction Flow

AI security expressions evaluate the ZeroTrustAction generated by the AI analysis pipeline. The following diagram shows how a request flows from initial analysis to authorization decision:

ZeroTrustAction Evaluation Flow
HTTP Request Incoming request enters the filter chain
AI Analysis Pipeline Risk scoring, behavioral analysis, threat detection
ZeroTrustAction Generated ALLOW | BLOCK | CHALLENGE | ESCALATE | PENDING_ANALYSIS
Repository-backed shared state Shared Zero Trust action lookup used across subsequent evaluations
Expression Evaluation #ai.isAllowed(), #ai.isBlocked(), etc.
Authorization Decision Grant or deny access based on expression result

Action Types

Action Meaning Typical Response
ALLOW AI assessment indicates low risk Grant access normally
BLOCK AI assessment indicates high risk or threat Deny access, log incident
CHALLENGE AI assessment indicates medium risk Require additional verification (MFA, CAPTCHA)
ESCALATE AI assessment requires human review Route to security team for manual approval
PENDING_ANALYSIS AI analysis is still in progress Apply default policy or wait for completion

Action Resolution Model

The public AI expression API is action-based. It does not expose a numeric riskScore(...) function. Runtime code resolves the current request to a Zero Trust action such as ALLOW, BLOCK, CHALLENGE, ESCALATE, or PENDING_ANALYSIS, and #ai.* expressions read that action.

Text
Resolved action = ALLOW | BLOCK | CHALLENGE | ESCALATE | PENDING_ANALYSIS

Examples:
  #ai.isAllowed()
  #ai.isBlocked()
  #ai.needsChallenge()
  #ai.hasActionIn('ALLOW', 'CHALLENGE')
  #ai.hasActionOrDefault('BLOCK', 'ALLOW')

Practical Scenarios

Scenario 1: Risk-Based Access Control

Use AI expressions to implement tiered access control based on risk level:

  • Low risk —AI returns ALLOW, access is granted
  • Medium risk —AI returns CHALLENGE, require additional authentication (MFA)
  • High risk —AI returns BLOCK, access is denied
SpEL
// Strict mode: only allow if AI explicitly approves
#ai.isAllowed()

// Permissive mode: allow unless AI explicitly blocks
!(#ai.isBlocked())

// Challenge-aware: allow if permitted or after challenge completion
#ai.hasActionIn('ALLOW', 'CHALLENGE')

Scenario 2: AI + Role-Based Hybrid

Combine AI risk assessment with traditional role-based access control for defense in depth:

SpEL
// Require both AI approval and appropriate role
#ai.isAllowed() and hasAnyAuthority('ROLE_USER', 'ROLE_ADMIN')

// Admin bypass with AI monitoring (AI can still block suspicious admin activity)
hasRole('ADMIN') and !(#ai.isBlocked())

// Elevated access requires both AI approval and specific authority
#ai.isAllowed() and hasAuthority('PERMISSION_SENSITIVE_DATA_READ')

Scenario 3: Ownership with AI Verification

Combine AI expressions with @Protectable ownership checking and permission evaluation for fine-grained access control:

Java
// AI + ownership + permission: triple-layered authorization
@Protectable(ownerField = "createdBy", sync = true)
@PreAuthorize("#ai.isAllowed() and hasPermission(#id, 'DOCUMENT', 'UPDATE')")
public void updateDocument(Long id, DocumentDto dto) { ... }

// AI + ownership for read operations
@Protectable(ownerField = "patientId")
@PreAuthorize("#ai.isAllowed() and hasPermission(#id, 'MEDICAL_RECORD', 'READ')")
public MedicalRecord getRecord(Long id) { ... }

// Fallback when AI is unavailable: default to deny
@Protectable(ownerField = "accountId")
@PreAuthorize("#ai.hasActionOrDefault('BLOCK', 'ALLOW') and hasPermission(#id, 'ACCOUNT', 'UPDATE')")
public void updateAccount(Long id, AccountDto dto) { ... }

Creating AI-Aware Policies in Admin

AI security expressions can be used in stored SpEL policies managed through the OSS IAM admin surfaces. In current OSS code, administrators work primarily through /admin/policy-center, selecting compatible conditions or entering #ai.* expressions directly.

Step 1: Open Policy Center

Navigate to /admin/policy-center and choose the appropriate creation flow for the target resource. For URL policies, define the target pattern and HTTP method. For method-oriented rules, start from the scanned resource list or related resource-admin entry points that redirect back into Policy Center.

Step 2: Select AI Condition Template

In the condition editor, use the supported #ai.* expressions directly or combine them with role, permission, time, and IP conditions. Typical action-based checks include:

  • AI Approved#ai.isAllowed()
  • AI Not Blocked!(#ai.isBlocked())
  • AI Approved + Role#ai.isAllowed() and hasRole('...')
  • Custom AI Expression —Write any valid SpEL using #ai.* functions

Step 3: Confirm Activation Status

Policies can move through approval states. In OSS code, PENDING and REJECTED policies are skipped by CustomDynamicAuthorizationManager; active runtime policies are typically APPROVED or NOT_REQUIRED.

  • NOT_REQUIRED —Loaded without an approval gate.
  • PENDING —Visible for review but not enforced at runtime.
  • APPROVED / REJECTED —Approved policies load into runtime mappings; rejected policies do not.

Step 4: Monitor Policy Effectiveness

After saving a policy, verify it through Policy Center lists, approval status, simulator, matrix analysis, and audit/security monitoring:

  • Confirm whether the policy is APPROVED, NOT_REQUIRED, PENDING, or REJECTED
  • Cross-check the resulting SpEL condition and target scope before rollout
  • Use the Policy Center simulator and matrix views to inspect expected outcomes
  • Review allow, deny, challenge, and escalation outcomes in audit and security monitoring flows

Cache Architecture

AI security expressions rely on cached ZeroTrustAction objects to avoid re-running AI analysis on every expression evaluation. The caching strategy differs between URL-level and method-level expressions.

Cache Layers

ZeroTrustAction Cache Lookup Order
Request Attribute Checked first — in-request cache for current HTTP request
Caffeine Local Cache Method-level only —5s TTL, 10K max entries
Repository-backed lookup Shared Zero Trust action repository, often Redis-backed in distributed deployments
Cache Layer Scope TTL Max Entries Used By
Request Attribute Single HTTP request Request lifetime 1 per request Both URL and Method level
Caffeine JVM-local 5 seconds 10,000 Method-level only
Repository-backed shared lookup Shared runtime state Repository implementation dependent Implementation dependent Both URL and Method level

The request attribute cache ensures that within a single HTTP request, the ZeroTrustAction is resolved only once regardless of how many expressions reference it. The Caffeine cache at the method level provides low-latency access for repeated evaluations across requests within a short time window.