ExCedar
ExCedar is an Elixir library that wraps the Cedar authorization policy engine via a NIF built with Rustler. It gives Elixir applications a fast, idiomatic interface to Cedar — evaluating authorization decisions against policies and entity stores — without requiring users to install a Rust toolchain, thanks to precompiled NIF artifacts distributed with the package.
Installation
Add ex_cedar to your dependencies in mix.exs:
def deps do
[
{:ex_cedar, "~> 0.1"}
]
end
Usage
Authorize
policy = """
permit(
principal == User::"alice",
action == Action::"view",
resource == Document::"doc1"
);
"""
entities = [
%ExCedar.Entity{
uid: ExCedar.EntityUid.new("User", "alice"),
attributes: %{},
parents: []
}
]
request = %ExCedar.Request{
principal: ExCedar.EntityUid.new("User", "alice"),
action: ExCedar.EntityUid.new("Action", "view"),
resource: ExCedar.EntityUid.new("Document", "doc1"),
context: %{}
}
{:ok, %ExCedar.Decision{decision: :allow}} = ExCedar.authorize(policy, entities, request)
For multiple requests against the same policy set, compile once and reuse the handles:
{:ok, ps} = ExCedar.PolicySet.compile(policy)
{:ok, ents} = ExCedar.Entities.from_list(entities)
{:ok, %ExCedar.Decision{decision: :allow}} =
ExCedar.Authorizer.authorize(ps, ents, request)
Validate
Use ExCedar.Schema and ExCedar.Validator to check that your policies are consistent
with your schema before deploying them:
schema_text = """
entity User;
entity Document;
action "view" appliesTo {
principal: [User],
resource: [Document],
context: {}
};
"""
{:ok, schema} = ExCedar.Schema.compile(schema_text)
{:ok, ps} = ExCedar.PolicySet.compile(policy)
{:ok, %ExCedar.ValidationResult{validated?: true, errors: [], warnings: _}} =
ExCedar.Validator.validate(ps, schema)
You can also pass a compiled schema to authorize to enable type-aware evaluation and
request shape validation:
ExCedar.Authorizer.authorize(ps, ents, request, schema: schema)
# or via the one-shot facade:
ExCedar.authorize(policy, entities, request, schema: schema_text)
Policy templates
Cedar supports template policies with ?principal and ?resource slots. Use
ExCedar.PolicySet.link_template/4 to bind slots and get a new, immutable handle.
Cedar assigns ids ("policy0", "policy1", ...) when parsing policy text, so
discover the template id with template_ids/1 rather than hardcoding it:
template = "permit(principal == ?principal, action, resource);"
{:ok, ps} = ExCedar.PolicySet.compile(template)
[template_id] = ExCedar.PolicySet.template_ids(ps)
principal = ExCedar.EntityUid.new("User", "alice")
{:ok, ps2} = ExCedar.PolicySet.link_template(ps, template_id, "alice_policy", %{principal: principal})
# ps is unchanged; ps2 includes the linked policy
ExCedar.PolicySet.policy_ids(ps2)
# => ["alice_policy"]
Handles
ExCedar.PolicySet.compile/1, ExCedar.Schema.compile/1, and ExCedar.Entities.from_list/1
return opaque NIF resource handles (ResourceArc). These handles are:
- Immutable — operations that "modify" a policy set (like
link_template/4) return a new handle; the original is unchanged. - Thread-safe — safe to share across processes or store in ETS/module attributes.
- Not persistent — handles do not survive a node restart. Recompile from source on
boot, for example in
Application.start/2or a supervised startup task.
Telemetry
ExCedar emits :telemetry span events around native operations. See
ExCedar.Telemetry for the full event contract.
Events:
[:ex_cedar, :authorize, :start | :stop | :exception][:ex_cedar, :compile, :start | :stop | :exception]
Precompiled NIFs
By default, ExCedar downloads a precompiled NIF for your platform at compile time — no Rust
toolchain required. To force a source build (for example, when developing ExCedar itself or
targeting an unsupported platform), set EX_CEDAR_BUILD=1 before compiling:
EX_CEDAR_BUILD=1 mix compile
Documentation is available at https://hexdocs.pm/ex_cedar.