# ConditionMatchers in CoSec: How Policy-Based Access Control Works

> Discover ConditionMatchers in CoSec to understand how policy-based access control works. Learn how components inspect requests and contexts to satisfy policies.

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

---

**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](https://github.com/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`](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/ConditionMatcher.kt):

```kotlin
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`](https://github.com/ahoo-wang/cosec/blob/main/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`](https://github.com/ahoo-wang/cosec/blob/main/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`](https://github.com/ahoo-wang/cosec/blob/main/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`](https://github.com/ahoo-wang/cosec/blob/main/MatcherFactoryRegister.kt) file in `cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/policy/` handles this auto-discovery:

```kotlin
// 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 via `BoolConditionMatcherFactory`
- **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 `EqConditionMatcherFactory` and `InConditionMatcherFactory`
- **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 `InTenantConditionMatcherFactory` and `InRoleConditionMatcherFactory`
- **SpEL / OGNL** – Evaluates Spring Expression Language or OGNL scripts against the request context via `SpelConditionMatcherFactory` and `OgnlConditionMatcherFactory`
- **RateLimiter / GroupedRateLimiter** – Applies token bucket or fixed-window rate limits via `RateLimiterConditionMatcherFactory` and `GroupedRateLimiterConditionMatcherFactory`

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:

1. **Policy parsing** – The policy engine reads the `condition` block from the JSON/YAML policy definition, extracting the `type` field and configuration parameters.

2. **Factory resolution** – The engine queries `ConditionMatcherFactoryProvider` to locate the registered factory matching the specified type.

3. **Matcher instantiation** – The factory creates the concrete `ConditionMatcher` instance, injecting the configuration (such as regex patterns or role lists).

4. **Evaluation** – CoSec invokes `match(request)` on the matcher. For composite matchers like `bool`, this recursively evaluates child matchers.

Consider this policy example that demonstrates composition:

```json
{
  "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:

1. **Implement the ConditionMatcher interface** – Create a class that implements `ConditionMatcher` and overrides the `match(request)` method with your custom evaluation logic.

2. **Create a factory** – Implement `ConditionMatcherFactory` with a unique `type` string identifier. The factory's `create(configuration)` method should instantiate your matcher with the provided configuration map.

3. **Register as a Spring bean** – Annotate your factory with `@Component` or define it as a bean in your Spring configuration. The `MatcherFactoryRegister` will automatically detect it during application startup, or you can manually register it via `ConditionMatcherFactoryProvider.register()`.

4. **Reference in policy** – Use your custom `type` identifier in policy JSON/YAML definitions:

```json
{
  "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 : RequestMatcher` interface in [`cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/ConditionMatcher.kt`](https://github.com/ahoo-wang/cosec/blob/main/cosec-api/src/main/kotlin/me/ahoo/cosec/api/policy/ConditionMatcher.kt) to 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/condition` cover 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`](https://github.com/ahoo-wang/cosec/blob/main/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.