Action Matchers
Action matchers determine whether an incoming request matches the action pattern defined in a policy statement. CoSec provides three built-in matcher types and an SPI (Service Provider Interface) for custom implementations. All action matchers are resolved at policy load time via the ActionMatcherFactory SPI.
ActionMatcher Interface
ActionMatcher extends RequestMatcher:
interface ActionMatcher : RequestMatcherThe RequestMatcher interface defines:
fun match(request: Request, securityContext: SecurityContext): BooleanBuilt-In Action Matchers
PathActionMatcher
PathActionMatcher uses Spring's PathPattern for URL pattern matching with path variable extraction:
class PathActionMatcher(
private val patternParser: PathPatternParser,
private val pathPattern: PathPattern,
configuration: Configuration
) : AbstractActionMatcher(PathActionMatcherFactory.TYPE, configuration)When a match succeeds, extracted path variables are automatically stored in the SecurityContext:
val pathMatchInfo = pathPattern.matchAndExtract(pathContainer) ?: return false
securityContext.setPathVariables(pathMatchInfo.uriVariables)This makes path variables available to condition matchers via the request.path.var.xxx part extractor.
AbstractActionMatcher
AbstractActionMatcher adds HTTP method filtering:
abstract class AbstractActionMatcher(
override val type: String,
final override val configuration: Configuration
) : ActionMatcher {
val method: Set<String> = configuration.asMethod()
override fun match(request, securityContext): Boolean {
if (method.isNotEmpty() && !method.contains(request.method.uppercase())) return false
return internalMatch(request, securityContext)
}
}The method configuration key accepts a single method ("GET") or a list (["GET", "POST"]).
AllActionMatcher
AllActionMatcher matches every request:
override fun internalMatch(request: Request, securityContext: SecurityContext): Boolean = trueTriggered by the wildcard "*" pattern.
CompositeActionMatcher
CompositeActionMatcher combines multiple matchers using OR logic:
override fun match(request: Request, securityContext: SecurityContext): Boolean =
actionMatchers.any { it.match(request, securityContext) }Created automatically when a policy action defines multiple path patterns.
ReplaceablePathActionMatcher
ReplaceablePathActionMatcher supports SpEL template expressions in path patterns:
class ReplaceablePathActionMatcher(
private val patternParser: PathPatternParser,
private val pattern: String,
configuration: Configuration
) : AbstractActionMatcher(PathActionMatcherFactory.TYPE, configuration)When the pattern contains SpEL templates (e.g., "#{context.principal.attributes.customPath}"), the pattern is resolved at runtime from the security context, enabling dynamic path patterns per user or tenant.
SPI: ActionMatcherFactory
ActionMatcherFactory is the SPI interface for creating matchers:
interface ActionMatcherFactory {
val type: String
fun create(configuration: Configuration): ActionMatcher
}ActionMatcherFactoryProvider
ActionMatcherFactoryProvider discovers factories via Java SPI (ServiceLoader):
object ActionMatcherFactoryProvider {
init {
ServiceLoader.load(ActionMatcherFactory::class.java)
.forEach { register(it) }
}
}Built-in factories are registered in META-INF/services/me.ahoo.cosec.policy.action.ActionMatcherFactory:
| Factory | Type | Matcher |
|---|---|---|
PathActionMatcherFactory | "path" | URL pattern matching |
AllActionMatcherFactory | "all" | Wildcard matching |
CompositeActionMatcherFactory | "composite" | OR combination |
Registering Custom Matchers
- Implement
ActionMatcherFactorywith a uniquetypestring - Create the file
META-INF/services/me.ahoo.cosec.policy.action.ActionMatcherFactory - Add the fully qualified class name of your factory
Architecture Diagrams
Action Matcher Class Hierarchy
classDiagram
direction TB
class RequestMatcher {
<<interface>>
+match(request, context): Boolean
}
class ActionMatcher {
<<abstract>>
+type: String
+configuration: Configuration
+method: Set~String~
+match(request, context): Boolean
#internalMatch(request, context): Boolean
}
class PathActionMatcher {
-patternParser: PathPatternParser
-pathPattern: PathPattern
#internalMatch(request, context): Boolean
}
class ReplaceablePathActionMatcher {
-pattern: String
-expression: Expression
#internalMatch(request, context): Boolean
}
class AllActionMatcher {
+INSTANCE: AllActionMatcher
#internalMatch(): Boolean = true
}
class CompositeActionMatcher {
-actionMatchers: List~ActionMatcher~
+match(request, context): Boolean
}
class ActionMatcherFactory {
<<interface>>
+type: String
+create(config): ActionMatcher
}
class PathActionMatcherFactory {
+type = path
}
class AllActionMatcherFactory {
+type = all
}
class CompositeActionMatcherFactory {
+type = composite
}
class ActionMatcherFactoryProvider {
<<object>>
-factories: ConcurrentHashMap
+register(factory)
+getRequired(type): ActionMatcherFactory
}
RequestMatcher <|-- ActionMatcher
ActionMatcher <|.. AbstractActionMatcher
ActionMatcher <|.. CompositeActionMatcher
AbstractActionMatcher <|-- PathActionMatcher
AbstractActionMatcher <|-- ReplaceablePathActionMatcher
AbstractActionMatcher <|-- AllActionMatcher
ActionMatcherFactory <|.. PathActionMatcherFactory
ActionMatcherFactory <|.. AllActionMatcherFactory
ActionMatcherFactory <|.. CompositeActionMatcherFactory
ActionMatcherFactoryProvider --> ActionMatcherFactory : manages
PathActionMatcherFactory ..> PathActionMatcher : creates
AllActionMatcherFactory ..> AllActionMatcher : creates
CompositeActionMatcherFactory ..> CompositeActionMatcher : createsFactory SPI Resolution Sequence
sequenceDiagram
autonumber
participant Policy as PolicyLoader
participant Config as Configuration
participant Provider as ActionMatcherFactoryProvider
participant Factory as ActionMatcherFactory
participant Matcher as ActionMatcher
Policy->>Config: get action configuration
Policy->>Provider: getRequired("path")
Provider-->>Factory: PathActionMatcherFactory
Policy->>Factory: create(configuration)
Factory->>Factory: parse PathPattern
Factory-->>Matcher: PathActionMatcher
Matcher-->>Policy: ready for usePath Matching with Variable Extraction
flowchart TD
A["PathActionMatcher.match(request, context)"] --> B{"method filter"}
B -->|"method not matched"| C["return false"]
B -->|"method matched or empty"| D["parsePath(request.path)"]
D --> E["pathPattern.matchAndExtract(pathContainer)"]
E --> F{"match found?"}
F -->|"no"| G["return false"]
F -->|"yes"| H["setPathVariables(uriVariables)"]
H --> I["return true"]
style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style E fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style G fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style H fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style I fill:#2d333b,stroke:#6d5dfc,color:#e6edf3Policy JSON Examples
Single path pattern
{
"action": {
"path": {
"pattern": "/api/users/**",
"method": ["GET", "POST"]
}
}
}Multiple path patterns (CompositeActionMatcher)
{
"action": {
"path": {
"pattern": ["/api/users/**", "/api/admin/**"]
}
}
}Wildcard (AllActionMatcher)
{
"action": "*"
}References
- PathActionMatcher.kt:42 - Path-based action matching with variable extraction
- AllActionMatcher.kt:30 - Wildcard matcher
- CompositeActionMatcher.kt:32 - OR-combined matcher
- ActionMatcherFactory.kt:30 - Factory SPI interface
- ActionMatcherFactoryProvider.kt:20 - SPI provider with ServiceLoader
Related Pages
- Policy Evaluation - How action matchers are used in statement verification
- Condition Matchers - Condition evaluation after action matching
- Authorization Flow - Full authorization pipeline
- Permissions and Roles - Permission-level action matching