Version: v0.13
Cedarling Integration
Cedarling is a sidecar that exposes the Cedar policy engine via the OpenID AuthZEN protocol. The EDK provides a dedicated client for Cedarling integration.
Overview
Cedar is a policy language developed by AWS that provides:
- Readable policies - Human-friendly syntax for authorization rules
- Fast evaluation - Millisecond-level decision times
- Formal verification - Policies can be mathematically proven correct
Cedarling wraps Cedar in an AuthZEN-compliant REST API, enabling integration with the EDK's authorization framework.
Architecture
Configuration
CedarlingConfig
data class CedarlingConfig(
val baseUrl: String, // Cedarling base URL
val evaluationPath: String = "/cedarling/evaluation", // Evaluation endpoint
val healthPath: String = "/health", // Health check endpoint
val discoveryPath: String = "/.well-known/authzen-configuration",
val evaluationTimeoutMs: Long = 5000,
val healthCheckTimeoutMs: Long = 2000
)
Factory Methods
// Local development
val localConfig = CedarlingConfig.LOCAL
// Equivalent to: CedarlingConfig(baseUrl = "http://localhost:5000")
// From environment variables
val envConfig = CedarlingConfig.fromEnvironment(
baseUrl = System.getenv("CEDARLING_URL") ?: "http://localhost:5000",
evaluationTimeoutMs = 3000
)
Spring Boot Configuration
sphereon:
authzen:
enabled: true
pdp:
type: cedarling
base-url: http://cedarling-sidecar:5000
evaluation-path: /cedarling/evaluation
health-path: /health
timeout-ms: 5000
health-check-timeout-ms: 2000
CedarlingPdp
The Cedarling PDP client implementation:
class CedarlingPdp(
private val config: CedarlingConfig,
private val httpClient: HttpClient = createDefaultClient()
) : AuthZenPdp {
override val id: String = "cedarling"
override suspend fun isHealthy(): Boolean {
return try {
withTimeout(config.healthCheckTimeoutMs) {
val response = httpClient.get("${config.baseUrl}${config.healthPath}")
response.status.isSuccess()
}
} catch (e: Exception) {
false
}
}
override suspend fun evaluate(
request: AuthZenEvaluationRequest
): IdkResult<AuthZenEvaluationResponse, IdkError> {
return try {
withTimeout(config.evaluationTimeoutMs) {
val response = httpClient.post("${config.baseUrl}${config.evaluationPath}") {
contentType(ContentType.Application.Json)
setBody(request)
}
if (response.status.isSuccess()) {
Ok(response.body<AuthZenEvaluationResponse>())
} else {
Err(IdkError(
code = "CEDARLING_PDP_ERROR",
message = "PDP returned ${response.status}"
))
}
}
} catch (e: TimeoutCancellationException) {
Err(IdkError(code = "CEDARLING_TIMEOUT", message = "Request timed out"))
} catch (e: Exception) {
Err(IdkError(code = "CEDARLING_ERROR", message = e.message ?: "Unknown error"))
}
}
}
Usage Examples
Basic Evaluation
import com.sphereon.authz.authzen.cedarling.CedarlingPdp
import com.sphereon.authz.authzen.cedarling.CedarlingConfig
import com.sphereon.authz.authzen.model.*
val config = CedarlingConfig(baseUrl = "http://localhost:5000")
val pdp = CedarlingPdp(config)
// Check access
val request = AuthZenEvaluationRequest(
subject = AuthZenSubject.user("alice"),
action = AuthZenAction(name = "read"),
resource = AuthZenResource(type = "document", id = "doc-123"),
context = AuthZenContext(tenantId = "acme", sessionId = "session-abc")
)
val result = pdp.evaluate(request)
when (result) {
is Ok -> {
if (result.value.decision) {
println("Access granted")
} else {
println("Access denied")
}
}
is Err -> println("Error: ${result.error.message}")
}
With Resilience
import com.sphereon.authz.authzen.impl.ResilientAuthZenPdp
val resilientPdp = ResilientAuthZenPdp(
delegate = CedarlingPdp(config),
config = AuthZenConfig(
enabled = true,
fallbackPolicy = FallbackPolicy.DENY,
cacheEnabled = true,
cacheTtlSeconds = 300,
resilience = ResilienceConfig(
circuitBreakerEnabled = true,
failureThreshold = 5,
resetTimeMs = 30000
)
)
)
// Evaluate with automatic caching and circuit breaking
val result = resilientPdp.evaluate(request)
// Monitor health
println("Circuit state: ${resilientPdp.getCircuitBreakerState()}")
println("Cache stats: ${resilientPdp.getCacheStats()}")
As PolicyEngine
import com.sphereon.authz.policy.AuthZenPolicyEngine
// Wrap in generic PolicyEngine interface
val policyEngine: PolicyEngine = AuthZenPolicyEngine(resilientPdp)
// Use PolicyRequest instead of AuthZenEvaluationRequest
val policyRequest = PolicyRequest(
principal = PolicyPrincipal(type = "User", id = "alice"),
action = PolicyAction(name = "document.read"),
resource = PolicyResource(type = "Document", id = "doc-123"),
context = PolicyContext(tenantId = "acme")
)
val decision = policyEngine.evaluate(policyRequest).getOrThrow()
if (decision.isPermitted) {
// Proceed with operation
}
Cedar Policy Examples
Basic RBAC
// Allow admins to perform any action
permit (
principal in Role::"admin",
action,
resource
);
// Allow users to read their own documents
permit (
principal,
action == Action::"read",
resource
) when {
resource.owner == principal
};
Tenant Isolation
// Only allow access within same tenant
permit (
principal,
action,
resource
) when {
principal.tenant_id == resource.tenant_id
};
Command-Based Authorization
// Allow specific command patterns
permit (
principal in Role::"document-editor",
action,
resource
) when {
action.domain == "document" &&
action.operation in ["create", "read", "update"]
};
// Deny delete for non-owners
forbid (
principal,
action,
resource
) when {
action.operation == "delete" &&
resource.owner != principal
};
Deployment
Docker Compose
services:
cedarling:
image: janssenproject/cedarling:latest
ports:
- "5000:5000"
volumes:
- ./policies:/policies
environment:
- CEDAR_POLICY_DIR=/policies
app:
build: .
environment:
- CEDARLING_URL=http://cedarling:5000
depends_on:
- cedarling
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
image: my-app:latest
env:
- name: CEDARLING_URL
value: http://localhost:5000
- name: cedarling
image: janssenproject/cedarling:latest
ports:
- containerPort: 5000
volumeMounts:
- name: policies
mountPath: /policies
volumes:
- name: policies
configMap:
name: cedar-policies
Error Handling
The Cedarling client uses specific error codes:
| Error Code | Description |
|---|---|
CEDARLING_PDP_ERROR | HTTP error from Cedarling |
CEDARLING_TIMEOUT | Request exceeded timeout |
CEDARLING_ERROR | General communication error |
CEDARLING_DISCOVERY_ERROR | Failed to fetch configuration |
val result = pdp.evaluate(request)
when (result) {
is Ok -> handleSuccess(result.value)
is Err -> {
when (result.error.code) {
"CEDARLING_TIMEOUT" -> {
// PDP slow, use cached result or fallback
}
"CEDARLING_PDP_ERROR" -> {
// PDP returned error, check policies
}
else -> {
// Network or other error
}
}
}
}
Health Monitoring
// Check if Cedarling is healthy
val healthy = pdp.isHealthy()
// In Spring Boot, expose as actuator endpoint
@Component
class CedarlingHealthIndicator(
private val pdp: CedarlingPdp
) : HealthIndicator {
override fun health(): Health = runBlocking {
if (pdp.isHealthy()) {
Health.up().build()
} else {
Health.down().withDetail("error", "Cedarling unavailable").build()
}
}
}