A Cedar policy engine — parser, evaluator, and authorizer in MoonBit.
Dependencies
moon add jaredzhou/mooncedar@0.1.2// 1. Parse a Cedar policy
let policies = parse_policies(
#|permit (principal == User::"alice", action == Action::"view", resource in Album::"jane_vacation");|
)
// 2. Build an entity store from JSON
let entities_src =
#|[{"uid":{"type":"User","id":"alice"},"attrs":{},"tags":{},"parents":[]},{"uid":{"type":"Photo","id":"VacationPhoto94.jpg"},"attrs":{},"tags":{},"parents":[{"type":"Album","id":"jane_vacation"}]}]
let store : MapEntityStore = @json.from_json(@json.parse(entities_src))
// 3. Create a request
let req = Request::{
principal: @evaluator.concrete_uid("User", "alice"),
action: @evaluator.concrete_uid("Action", "view"),
resource: @evaluator.concrete_uid("Photo", "VacationPhoto94.jpg"),
context: Context::Concrete(Value::Record(Map([]))),
}
// 4. Authorize
let result = is_authorized(req, policies.iter(), store)
match result.decision {
Decision::Allow => println("permitted")
Decision::Deny => println("denied")
}
// => permitted| Package | Purpose |
|---|---|
| jaredzhou/mooncedar | Unified API: parse_policies, stringify, builder fns, type aliases (Expr, Policy, Request, Value, ...), MapEntityStore, is_authorized, evaluate, reauthorize |
| jaredzhou/mooncedar/ast | Core AST: Expr, Policy, Entity, EntityUID, Value, PartialValue, Type + builder methods |
| jaredzhou/mooncedar/evaluator | EntityStore trait, EvalError, Request, Context, EntityUIDEntry, helpers (concrete_uid, unknown_uid) |
| jaredzhou/mooncedar/parser | Lexer, recursive descent parser, stringify |
let e = expr_principal()
.eq(expr_str("alice"))
.and_(expr_resource()
.has_tag(expr_str("confidential"))
)let policy = default_policy()
.permit()
.principal_eq("User", "alice")
.action_eq("Action", "view")
.resource_in("Album", "photos")
.when_(expr_resource().has_tag(expr_str("public")))let errors = @ast.validate_policies(policies)// In-memory store (built-in)
let store = new_map_store()
// From Cedar JSON
let store : MapEntityStore = @json.from_json(@json.parse(entities_src))
// Custom backend via pub(open) trait
struct DbStore { conn : Connection }
pub impl @evaluator.EntityStore for DbStore with get_entity(self, uid) {
db_lookup(self.conn, uid)
}let answer = evaluate(req, policies.iter(), store1) // partial result
let answer = answer.reauthorize(req, store2, mapping) // fill unknowns
let result = answer.concretize() // final decision// Strings: "Type::\"id\"" → Concrete, "Type" → Unknown
// Objects: {"type":"...","id":"..."} → Concrete
let src =
#|{"principal":"User::\"alice\"","action":"Action::\"view\"","resource":"Photo::\"x\"","context":{}}|
let dto : RequestJSON = @json.from_json(@json.parse(src))
let req : Request = dto.to_request()
let json = to_request_json(req).to_json()let src = stringify(policies)pub(all) struct AuthorizationResult {
decision : Decision
determining_policies : Array[DiagnosticReason]
errors : Array[DiagnosticError]
} derive(Eq, Debug)impl EntityStore for MapEntityStoreimpl ToJson for MapEntityStoreimpl FromJson for MapEntityStorepub(all) struct PartialAuthorizationAnswer {
satisfied : Array[DecisionRecord]
residuals : Array[ResidualRecord]
errors : Array[DiagnosticError]
} derive(Debug)fn[S : EntityStore] PartialAuthorizationAnswer::reauthorize(self : PartialAuthorizationAnswer, req : Request, store : S, mapping : Map[String, Value]) -> PartialAuthorizationAnswer{
"principal":"GitApp::User::\"JaneDoe\"",
"action":"Action::\"view\"",
"resource":"Photo::\"x\"",
"context":{"is_admin":true}
}impl ToJson for RequestJSONimpl FromJson for RequestJSONfn[S : EntityStore] eval_expr(expr : Expr, req : Request, store : S) -> PartialValue raise EvalErrorfn[S : EntityStore] evaluate(req : Request, policies : Iter[Policy], store : S) -> PartialAuthorizationAnswerfn[S : EntityStore] is_authorized(req : Request, policies : Iter[Policy], store : S) -> AuthorizationResultA Cedar policy engine — parser, evaluator, and authorizer in MoonBit.
Dependencies