ConditionMatchers in CoSec: How Policy-Based Access Control Works
ConditionMatchers in CoSec are lightweight, pluggable components that inspect incoming requests and security contexts, returning true or false to determine whether access-control policies are satisfied.
ConditionMatchers form the evaluative backbone of the ahoo-wang/cosec authorization framework, enabling declarative security rules through JSON or YAML policy definitions. These components implement a standardized interface that allows the policy engine to compose granular checks—ranging from authentication status to request attributes—into complex authorization decisions without modifying core engine code.
What Are ConditionMatchers?
A ConditionMatcher is a contract that extends RequestMatcher and defines the runtime evaluation logic for policy conditions. Every matcher implements the interface located at cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/ConditionMatcher.kt:
interface ConditionMatcher : RequestMatcher
When CoSec evaluates an access-control policy, it invokes the match(request) method on the appropriate ConditionMatcher. The method returns true if the request satisfies the specific rule (such as matching a path pattern or possessing a required role), otherwise false. This boolean result drives the subsequent authorization decision, determining whether to allow or deny the request.
Core Architecture Components
The ConditionMatcher ecosystem relies on three primary abstractions that separate policy definition from runtime execution.
The ConditionMatcher Interface
The base contract ConditionMatcher extends RequestMatcher and serves as the runtime evaluation point for all condition logic. Located in cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/ConditionMatcher.kt, this interface ensures that every matcher, whether built-in or custom, adheres to a consistent method signature for request inspection.
ConditionMatcherFactory
Each matcher type requires a corresponding ConditionMatcherFactory that knows how to construct the matcher from a configuration object. Defined in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/ConditionMatcherFactory.kt, factories parse the JSON/YAML policy fragments and instantiate the concrete matcher with the appropriate parameters (such as regex patterns, role names, or comparison values).
ConditionMatcherFactoryProvider
The ConditionMatcherFactoryProvider acts as the central registry, mapping the type string from policy definitions to the corresponding factory implementation. This provider is implemented in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/ConditionMatcherFactoryProvider.kt and maintains a lookup table that enables runtime discovery of matcher implementations.
MatcherFactoryRegister
Factory registration occurs automatically through Spring's bean scanning mechanism. The MatcherFactoryRegister.kt file in cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/policy/ handles this auto-discovery:
// MatcherFactoryRegister.kt
applicationContext.getBeansOfType(ConditionMatcherFactory::class.java)
.values.forEach { ConditionMatcherFactoryProvider.register(it) }
This registration process ensures that all ConditionMatcherFactory beans present in the application context are available to the policy engine at runtime.
Built-in ConditionMatcher Types
CoSec provides a comprehensive library of built-in matchers located under me.ahoo.cosec.policy.condition in the cosec-core module. These cover common authorization scenarios:
- Bool – Logical composition (
and,or,not) of other matchers viaBoolConditionMatcherFactory - All – Matches any request, serving as a default "allow" condition via
AllConditionMatcherFactory - Eq / In / StartsWith / EndsWith / Contains / Regex – Value comparisons on request attributes (headers, query parameters) via factories such as
EqConditionMatcherFactoryandInConditionMatcherFactory - Path – Ant-style path matching for request URIs via
PathConditionMatcherFactory - Authenticated – Verifies the request has an authenticated principal via
AuthenticatedConditionMatcherFactory - InTenant / InRole – Checks tenant membership or role assignment via
InTenantConditionMatcherFactoryandInRoleConditionMatcherFactory - SpEL / OGNL – Evaluates Spring Expression Language or OGNL scripts against the request context via
SpelConditionMatcherFactoryandOgnlConditionMatcherFactory - RateLimiter / GroupedRateLimiter – Applies token bucket or fixed-window rate limits via
RateLimiterConditionMatcherFactoryandGroupedRateLimiterConditionMatcherFactory
All concrete implementations reside in cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition.
Runtime Evaluation Flow
When a request arrives, CoSec processes the ConditionMatcher through the following pipeline:
-
Policy parsing – The policy engine reads the
conditionblock from the JSON/YAML policy definition, extracting thetypefield and configuration parameters. -
Factory resolution – The engine queries
ConditionMatcherFactoryProviderto locate the registered factory matching the specified type. -
Matcher instantiation – The factory creates the concrete
ConditionMatcherinstance, injecting the configuration (such as regex patterns or role lists). -
Evaluation – CoSec invokes
match(request)on the matcher. For composite matchers likebool, this recursively evaluates child matchers.
Consider this policy example that demonstrates composition:
{
"id": "read-profile",
"action": { "type": "allow" },
"condition": {
"type": "bool",
"and": [
{ "type": "authenticated" },
{ "type": "inRole", "roles": ["USER", "ADMIN"] },
{ "type": "eq", "key": "request.method", "value": "GET" },
{ "type": "path", "pattern": "/api/profile/**" }
]
}
}
In this example, BoolConditionMatcherFactory creates a BoolConditionMatcher that ensures all child conditions succeed: the user must be authenticated, possess the USER or ADMIN role, send a GET request, and access a path under /api/profile/.
Extending ConditionMatchers
Developers can implement custom authorization logic by creating new ConditionMatchers without modifying the CoSec core engine. Follow this four-step process:
-
Implement the ConditionMatcher interface – Create a class that implements
ConditionMatcherand overrides thematch(request)method with your custom evaluation logic. -
Create a factory – Implement
ConditionMatcherFactorywith a uniquetypestring identifier. The factory'screate(configuration)method should instantiate your matcher with the provided configuration map. -
Register as a Spring bean – Annotate your factory with
@Componentor define it as a bean in your Spring configuration. TheMatcherFactoryRegisterwill automatically detect it during application startup, or you can manually register it viaConditionMatcherFactoryProvider.register(). -
Reference in policy – Use your custom
typeidentifier in policy JSON/YAML definitions:
{
"condition": {
"type": "customIpAllowlist",
"allowedIps": ["192.168.1.0/24"]
}
}
This plugin architecture supports specialized requirements such as IP allow-listing, time-of-day restrictions, or custom header validation.
Summary
- ConditionMatchers implement the
ConditionMatcher : RequestMatcherinterface incosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/ConditionMatcher.ktto evaluate request attributes and security contexts. - ConditionMatcherFactory implementations construct matchers from policy configurations, while ConditionMatcherFactoryProvider maintains the runtime registry of available factories.
- Built-in matchers in
cosec-core/src/main/kotlin/me/ahoo/cosec/policy/conditioncover authentication, path matching, role verification, rate limiting, and logical composition via the Bool matcher. - MatcherFactoryRegister in the Spring Boot starter automatically discovers and registers factories via Spring's application context.
- Custom matchers integrate seamlessly by implementing the interface, providing a factory, and registering as a Spring bean, enabling domain-specific authorization logic without engine modifications.
Frequently Asked Questions
How do ConditionMatchers differ from ActionMatchers in CoSec?
ConditionMatchers evaluate the context and attributes of a request (such as headers, authentication status, or path patterns) to determine if a policy applies, whereas ActionMatchers typically evaluate the action being performed (such as HTTP method or resource operation). ConditionMatchers focus on when a policy should be evaluated, while ActionMatchers focus on what is being accessed. Both implement similar factory patterns but serve different phases of the authorization decision.
Can I combine multiple conditions in a single CoSec policy?
Yes, CoSec supports complex condition composition through the Bool matcher type. The BoolConditionMatcher (created by BoolConditionMatcherFactory) accepts and, or, and not arrays containing child matchers. This allows you to construct sophisticated rules requiring multiple attributes to match simultaneously, alternative conditions to satisfy, or specific conditions to be excluded, all within a single policy condition block.
Where does CoSec store the JSON schema for condition definitions?
CoSec provides a formal JSON Schema that describes valid condition structures in schema/condition.schema.json at the repository root. This schema defines the required fields, valid matcher types, and configuration parameters for all built-in ConditionMatchers, enabling IDE autocompletion and validation for policy files during development.
What is the performance impact of using SpEL or OGNL ConditionMatchers?
SpEL (Spring Expression Language) and OGNL (Object-Graph Navigation Language) matchers offer maximum flexibility for complex logic but incur higher evaluation overhead compared to simple matchers like eq or path. According to the source implementation in SpelConditionMatcherFactory and OgnlConditionMatcherFactory, these matchers parse and evaluate expressions at runtime against the security context. For high-throughput applications, prefer compiled matchers (such as PathConditionMatcher or InRoleConditionMatcher) for critical paths, reserving script-based matchers for administrative or low-frequency endpoints.
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 →