System Architecture
Overview of cedar4s components and how they interact.
High-Level Architecture
Core Components
CedarEngine[F]
Wraps the Cedar-Java authorization engine:
trait CedarEngine[F[_]] {
def authorize(request: CedarRequest, entities: CedarEntities): F[CedarDecision]
def authorizeBatch(requests: Seq[CedarRequest], entities: CedarEntities): F[Seq[CedarDecision]]
}
Responsibilities:
- Load and manage Cedar policies
- Execute authorization requests against policies
- Return allow/deny decisions with diagnostics
CedarSession[F]
Orchestrates authorization checks:
trait CedarSession[F[_]] {
def run(request: AuthCheck[?, ?, ?]): F[Either[CedarAuthError, Unit]]
def require(request: AuthCheck[?, ?, ?]): F[Unit]
def isAllowed(request: AuthCheck[?, ?, ?]): F[Boolean]
def batchIsAllowed(requests: Seq[AuthCheck[?, ?, ?]]): F[Seq[Boolean]]
def filterAllowed[A](items: Seq[A])(toRequest: A => AuthCheck[?, ?, ?]): F[Seq[A]]
}
Responsibilities:
- Convert
AuthCheckto Cedar request format - Load required entities via
EntityStore - Execute authorization via
CedarEngine - Handle errors and return results
EntityStore[F]
Manages entity loading:
trait EntityStore[F[_]] {
def loadForRequest(principal: CedarPrincipal, resource: ResourceRef): F[CedarEntities]
def loadForBatch(principal: CedarPrincipal, resources: Seq[ResourceRef]): F[CedarEntities]
def loadEntity(entityType: String, entityId: String): F[Option[CedarEntity]]
def loadEntityWithParents(entityType: String, entityId: String): F[Option[(CedarEntity, List[(String, String)])]]
}
Responsibilities:
- Route entity requests to appropriate fetchers
- Manage entity caching
- Support batch loading for performance
EntityFetcher[F, E, Id]
Loads entities from data sources:
trait EntityFetcher[F[_], E <: CedarEntity, Id] {
def fetch(id: Id): F[Option[E]]
def fetchBatch(ids: Set[Id])(using Applicative[F]): F[Map[Id, E]]
}
Responsibilities:
- Load domain entities from database/API
- Convert to Cedar entity format
- Support efficient batch loading
Request Flow
Simple Authorization
Deferred Authorization
Batch Authorization
Module Structure
cedar4s/
├── modules/
│ ├── core/ # Core types and interfaces
│ │ ├── auth/ # CedarSession, AuthCheck
│ │ ├── entities/ # EntityStore, EntityFetcher, EntityCache
│ │ ├── engine/ # CedarEngine wrapper
│ │ └── capability/ # Effect type classes (Sync, Concurrent)
│ │
│ ├── types/ # Shared types (CedarEntity, CedarValue)
│ │
│ ├── codegen/ # Code generation from Cedar schemas
│ │
│ ├── caffeine/ # Caffeine cache implementation
│ │
│ ├── sbt-plugin/ # sbt plugin for code generation
│ │
│ └── bench/ # JMH benchmarks
│
└── website/ # Documentation (Docusaurus)
Extension Points
Custom EntityFetcher
Load entities from any data source:
class ApiDocumentFetcher(client: HttpClient)
extends EntityFetcher[Future, Entities.Document, String] {
def fetch(id: String): Future[Option[Entities.Document]] =
client.get(s"/documents/$id").map(...)
}
Custom EntityCache
Implement distributed caching:
class RedisEntityCache(redis: RedisClient) extends EntityCache[Future] {
def get(uid: CedarEntityUid): Future[Option[CedarEntity]] = ...
def put(entity: CedarEntity): Future[Unit] = ...
// ...
}
Custom CedarSession
Add cross-cutting concerns:
class AuditingCedarSession(underlying: CedarSession[Future], auditLog: AuditLog)
extends CedarSession[Future] {
def run(request: AuthCheck[?, ?, ?]): Future[Either[CedarAuthError, Unit]] = {
underlying.run(request).map { result =>
auditLog.record(request, result)
result
}
}
}