Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cedar4j

Cedar policy engine for Java via WebAssembly.

Evaluates Cedar authorization policies in pure Java by compiling the Cedar Rust crate to Wasm and running it through Endive. No JNI, no native binaries — runs everywhere the JVM runs.

Usage

<dependency>
  <groupId>io.roastedroot</groupId>
  <artifactId>cedar4j</artifactId>
  <version>${cedar4j.version}</version>
</dependency>

Authorize

CedarEngine engine = CedarEngine.create();

AuthorizationRequest request = AuthorizationRequest.builder()
    .principal("User", "alice")
    .action("Action", "view")
    .resource("Document", "doc1")
    .build();

PolicySet policies = PolicySet.of(
    Policy.of("permit(principal, action, resource);", "allow-all"));

AuthorizationResponse response = engine.isAuthorized(
    request, policies, Collections.emptySet());

response.isAllowed();  // true
response.decision();   // Decision.ALLOW
response.reasons();    // ["allow-all"]

Entities and context

EntityUID alice = EntityUID.of("User", "alice");
EntityUID admins = EntityUID.of("Group", "admins");

Set<Entity> entities = Set.of(
    Entity.of(alice, Set.of(admins)),
    Entity.of(admins));

Map<String, Object> context = Map.of("authenticated", true);

AuthorizationRequest request = AuthorizationRequest.builder()
    .principal(alice)
    .action("Action", "delete")
    .resource("Document", "doc1")
    .context(context)
    .build();

PolicySet policies = PolicySet.of(Policy.of(
    "permit(principal, action, resource) when { principal in Group::\"admins\" };",
    "admin-access"));

AuthorizationResponse response = engine.isAuthorized(request, policies, entities);

Validation

Schema schema = Schema.fromCedar(
    "entity User;\n"
    + "entity Photo;\n"
    + "action viewPhoto appliesTo { principal: [User], resource: [Photo] };");

ValidationRequest request = ValidationRequest.builder()
    .schema(schema)
    .policies(policies)
    .build();

ValidationResponse response = engine.validate(request);
response.isValid();              // true if no validation errors
response.validationErrors();     // list of ValidationError

Caching

Pre-parse policies for repeated evaluations:

engine.cachePolicySet("my-policies", policies);

AuthorizationResponse response = engine.isAuthorizedCached(
    request, "my-policies", entities);

Schemas can be pre-parsed the same way. On the cached path the schema must be referenced by id — an inline request.schema() is rejected, because the underlying call only accepts a pre-parsed schema name:

engine.cacheSchema("my-schema", schema);

AuthorizationResponse response = engine.isAuthorizedCached(
    request, "my-policies", "my-schema", entities);

The cache lives inside the Wasm instance, so it is scoped to a single CedarEngine. To share it across a pool, cache on the pool rather than on a borrowed engine — see below.

Partial authorization

Evaluate with unknown principal, action, or resource:

PartialAuthorizationRequest partial = PartialAuthorizationRequest.builder()
    .action("Action", "view")
    .resource("Document", "doc1")
    .build();

PartialAuthorizationResponse response = engine.isAuthorizedPartial(
    partial, policies, entities);

response.decision();            // ALLOW or DENY
response.nontrivialResiduals(); // policies that couldn't be fully evaluated

Concurrent use

Wasm instances are not thread-safe. Use CedarEnginePool for concurrent access:

CedarEnginePool pool = CedarEnginePool.create(4);

try (CedarEnginePool.Loan loan = pool.borrow()) {
    AuthorizationResponse response = loan.engine().isAuthorized(
        request, policies, entities);
}

Cache on the pool, not on a borrowed engine — each engine has its own Wasm instance and therefore its own cache. The pool replays cached entries onto every engine it hands out, including ones it creates later:

pool.cachePolicySet("my-policies", policies);
pool.cacheSchema("my-schema", schema);

try (CedarEnginePool.Loan loan = pool.borrow()) {
    AuthorizationResponse response = loan.engine().isAuthorizedCached(
        request, "my-policies", "my-schema", entities);
}

Custom ObjectMapper

CedarEngine engine = CedarEngine.builder()
    .withObjectMapper(myCustomMapper)
    .build();

Raw JSON API

A low-level string-based API is available for advanced use cases via engine.raw():

String result = engine.raw().authorize(requestJson);
String parsed = engine.raw().parsePolicy(policyText);
String formatted = engine.raw().formatPolicies(policiesText);

See CedarRawEngine for the full list of raw methods.

Building

mvn clean verify

To rebuild the Wasm binary (requires Rust):

cd wasm-build
make all

License

Apache-2.0

About

Running Cedar in pure Java

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages