How CoSec Policy-Based Authorization Works: Architecture and Implementation Guide
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/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/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/Statement.kt), every statement includes:
- A descriptive name
- An effect value of either
ALLOWorDENY - A verify function that checks request-principal attributes
Condition Matching
The Condition interface in [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/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/cosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt) and follows a strict evaluation sequence:
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:
- Global policies via
policyRepository.getGlobalPolicy()(lines 63-68) - Principal-specific policies from
principal.policies(lines 70-77) - App-role permissions fetched via
appRolePermissionRepositorywhen 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 aPolicyVerifyContext(lines 56-71). - ALLOW statements are processed next. The first explicit allow (
VerifyResult.ALLOW) yields a successfulPolicyVerifyContext(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/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/cosec-cocache/src/main/kotlin/me/ahoo/cosec/cache/RedisPolicyRepository.kt)) implementsPolicyRepositoryfor persistent policy storage. - RedisAppRolePermissionRepository ([
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/cosec-openapi/src/main/kotlin/me/ahoo/cosec/openapi/generator/OpenAPIPolicyGenerator.kt) class provides:
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/:
{
"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:
@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:
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
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
ALLOWorDENYeffects. - The [
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
CoSecPolicyCacheAutoConfigurationensures 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.
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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →