# How CoSec Policy-Based Authorization Works: Architecture and Implementation Guide

> Discover how CoSec policy-based authorization works through its declarative JSON/YAML configuration model, evaluating policies, statements, and conditions for robust access control.

- Repository: [Ahoo Wang/cosec](https://github.com/ahoo-wang/cosec)
- Tags: architecture
- Published: 2026-02-23

---

**CoSec implements policy-based authorization by evaluating a hierarchy of policies, statements, conditions, and role-permissions against incoming requests using a declarative JSON/YAML configuration model.**

CoSec is an open-source security framework hosted at `ahoo-wang/cosec` that provides a comprehensive policy-based authorization system for JVM applications. The engine operates entirely on declarative policies that define access rules through JSON or YAML files, allowing developers to externalize authorization logic from application code.

## Policy Model Architecture

The CoSec authorization framework is built on four core components that form a hierarchical evaluation structure.

### Policy Component

A **Policy** serves as the top-level container holding an identifier, a list of statements, and a condition that determines whether the policy applies to a specific request. Defined in [[`Policy.kt`](https://github.com/ahoo-wang/cosec/blob/main/Policy.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/Policy.kt), this component represents the entry point for all authorization decisions.

### Statement and Effect

Each policy contains **Statements** that specify concrete access rules. According to [[`Statement.kt`](https://github.com/ahoo-wang/cosec/blob/main/Statement.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/Statement.kt), every statement includes:
- A descriptive name
- An **effect** value of either `ALLOW` or `DENY`
- A verify function that checks request-principal attributes

### Condition Matching

The **Condition** interface in [[`Condition.kt`](https://github.com/ahoo-wang/cosec/blob/main/Condition.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/condition/Condition.kt) provides reusable matchers for URL patterns, HTTP methods, IP addresses, and other request attributes. Conditions filter which policies and permissions participate in the evaluation for a given request.

### Role-Based Permissions

**AppRolePermission**, defined in [[`AppRolePermission.kt`](https://github.com/ahoo-wang/cosec/blob/main/AppRolePermission.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/permission/AppRolePermission.kt), maps role identifiers to permission sets. When a principal carries role identifiers, the system retrieves corresponding permissions and evaluates them as statements with effects.

## Authorization Verification Flow

The core authorization algorithm resides in [[`SimpleAuthorization.kt`](https://github.com/ahoo-wang/cosec/blob/main/SimpleAuthorization.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt) and follows a strict evaluation sequence:

```kotlin
class SimpleAuthorization(
    private val policyRepository: PolicyRepository,
    private val appRolePermissionRepository: AppRolePermissionRepository,
    private val blacklistChecker: BlacklistChecker = BlacklistChecker.NoOp
) : Authorization { … }

```

### Root User Shortcut

If the principal represents the root user, the request is automatically allowed without further evaluation (lines 52-58). This provides a super-admin bypass for emergency access scenarios.

### Blacklist Verification

Before policy evaluation, the system invokes a configurable `BlacklistChecker` that can reject requests based on predefined blocklists (lines 14-22). This check occurs at the entry point to prevent unnecessary processing of banned entities.

### Policy Retrieval and Matching

The engine loads policies through three channels:
1. **Global policies** via `policyRepository.getGlobalPolicy()` (lines 63-68)
2. **Principal-specific policies** from `principal.policies` (lines 70-77)
3. **App-role permissions** fetched via `appRolePermissionRepository` when the principal carries roles (lines 80-88)

Each candidate policy's condition is evaluated first through `policy.condition.match(...)`. Only policies with matching conditions proceed to statement inspection (lines 52-54).

### Statement Evaluation Logic

The verification engine applies **explicit deny precedence**:
- **DENY statements** are examined first. The first explicit deny (`VerifyResult.EXPLICIT_DENY`) short-circuits evaluation immediately, returning a `PolicyVerifyContext` (lines 56-71).
- **ALLOW statements** are processed next. The first explicit allow (`VerifyResult.ALLOW`) yields a successful `PolicyVerifyContext` (lines 75-90).

### Result Conversion

The `VerifyResult` contained in the `VerifyContext` transforms into an `AuthorizeResult` of either `ALLOW`, `EXPLICIT_DENY`, or `IMPLICIT_DENY` (lines 98-106). If no policy or permission matches, the request receives an **implicit deny**.

## Policy Storage and Caching

CoSec optimizes policy retrieval through a multi-layered caching architecture backed by Redis.

### Cache Configuration

`CoSecPolicyCacheAutoConfiguration` wires a Redis-backed `PolicyCache` and global-policy-index cache to ensure repeated lookups remain performant. This auto-configuration resides in [[`CoSecPolicyCacheAutoConfiguration.kt`](https://github.com/ahoo-wang/cosec/blob/main/CoSecPolicyCacheAutoConfiguration.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/authorization/cache/CoSecPolicyCacheAutoConfiguration.kt).

### Redis Repository Implementations

- **RedisPolicyRepository** ([[`RedisPolicyRepository.kt`](https://github.com/ahoo-wang/cosec/blob/main/RedisPolicyRepository.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-cocache/src/main/kotlin/me/ahoo/cosec/cache/RedisPolicyRepository.kt)) implements `PolicyRepository` for persistent policy storage.
- **RedisAppRolePermissionRepository** ([[`RedisAppRolePermissionRepository.kt`](https://github.com/ahoo-wang/cosec/blob/main/RedisAppRolePermissionRepository.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-cocache/src/main/kotlin/me/ahoo/cosec/cache/RedisAppRolePermissionRepository.kt)) handles role-permission mappings in Redis.

## OpenAPI Policy Generation

CoSec supports automatic policy generation from OpenAPI specifications, eliminating manual JSON authoring.

The [[`OpenAPIPolicyGenerator.kt`](https://github.com/ahoo-wang/cosec/blob/main/OpenAPIPolicyGenerator.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-openapi/src/main/kotlin/me/ahoo/cosec/openapi/generator/OpenAPIPolicyGenerator.kt) class provides:

```kotlin
val policy = OpenAPIPolicyGenerator.generate(openAPI)

```

This generates a complete policy containing statements for each API operation, deriving path patterns and HTTP methods directly from the specification.

## Implementation Examples

### Defining a Declarative Policy

Store JSON policy definitions under `src/main/resources/cosec-policy/`:

```json
{
  "id": "order-service-policy",
  "category": "APPLICATION",
  "name": "Order Service Policy",
  "description": "Controls access to order endpoints",
  "condition": { "pathPattern": "/orders/**" },
  "statements": [
    {
      "name": "allow-read",
      "effect": "ALLOW",
      "condition": { "httpMethod": "GET" },
      "resource": "order:read"
    },
    {
      "name": "deny-delete",
      "effect": "DENY",
      "condition": { "httpMethod": "DELETE" },
      "resource": "order:delete"
    }
  ]
}

```

`LocalPolicyLoader` automatically loads these files at startup.

### Spring Boot Integration

Enable authorization with the `@EnableCoSecAuthorization` annotation:

```kotlin
@SpringBootApplication
@EnableCoSecAuthorization
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

```

This annotation imports `CoSecAuthorizationAutoConfiguration`, which registers the `SimpleAuthorization` bean.

### Programmatic Authorization Checks

Inject the `Authorization` interface and verify requests programmatically:

```kotlin
val request = DefaultRequest(
    method = HttpMethod.POST,
    url = "/orders/123",
    appId = "order-service",
    spaceId = "default",
    headers = HttpHeaders().apply { 
        add("Authorization", "Bearer abc") 
    }
)

val principal = CoSecPrincipal(
    id = "user-42",
    roles = setOf("buyer"),
    policies = emptySet(),
    attributes = emptyMap()
)

val context = SecurityContext(principal = principal)

authorization.authorize(request, context)
    .subscribe { result ->
        println("Authorization result: $result")
    }

```

The `authorize()` method returns `AuthorizeResult.ALLOW`, `EXPLICIT_DENY`, or `IMPLICIT_DENY`.

### Generating Policies from OpenAPI

```kotlin
val openAPI = OpenAPIParser()
    .readLocation("petstore.yaml", null, ParseOptions())
val generatedPolicy = OpenAPIPolicyGenerator.generate(openAPI)
policyRepository.save(generatedPolicy)

```

## Summary

- **CoSec policy-based authorization** evaluates requests against hierarchical policies containing statements with explicit `ALLOW` or `DENY` effects.
- The [[`SimpleAuthorization.kt`](https://github.com/ahoo-wang/cosec/blob/main/SimpleAuthorization.kt)](https://github.com/ahoo-wang/cosec/blob/main/cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt) engine applies **explicit deny precedence**, checking root access, blacklists, global policies, principal policies, and role-permissions in sequence.
- **Conditions** filter applicable policies using matchers for paths, methods, and attributes before statement evaluation occurs.
- Redis-backed caching through `CoSecPolicyCacheAutoConfiguration` ensures high-performance policy retrieval in production environments.
- **OpenAPI integration** enables automatic policy generation from API specifications, reducing manual configuration overhead.

## Frequently Asked Questions

### How does CoSec handle conflicting allow and deny statements?

CoSec evaluates **DENY statements before ALLOW statements**. If any statement produces `VerifyResult.EXPLICIT_DENY`, the evaluation short-circuits immediately and returns a denial. Only if no deny matches does the engine check for explicit allows. This deny-override logic ensures restrictive access control as implemented in lines 56-90 of [`SimpleAuthorization.kt`](https://github.com/ahoo-wang/cosec/blob/main/SimpleAuthorization.kt).

### Can CoSec policies be loaded from sources other than JSON files?

Yes. While `LocalPolicyLoader` handles classpath JSON/YAML files, the `PolicyRepository` interface abstracts storage. The [`RedisPolicyRepository`](https://github.com/ahoo-wang/cosec/blob/main/cosec-cocache/src/main/kotlin/me/ahoo/cosec/cache/RedisPolicyRepository.kt) implementation enables dynamic policy management through Redis, and custom implementations can integrate with databases or configuration services.

### What is the difference between implicit deny and explicit deny in CoSec?

An **explicit deny** occurs when a matching policy statement with `effect: DENY` validates against the request. An **implicit deny** occurs when no policies or permissions match the request at all. Explicit denies are logged with context via `PolicyVerifyContext`, while implicit denies represent default secure behavior when no authorization rules apply.

### How does CoSec optimize policy evaluation performance?

CoSec employs a **multi-layer caching strategy** configured through `CoSecPolicyCacheAutoConfiguration`. Global policies and principal-specific policies are cached in Redis-backed caches to prevent repeated repository lookups. Additionally, condition matching occurs before statement evaluation, ensuring expensive permission checks only run for relevant policies.