Locy Use Case: RBAC with Priority Rules (Rust)¶
Resolve deny-vs-allow authorization conflicts with prioritized Locy clauses.
This notebook uses schema-first mode and mirrors the Python flow using the Rust API (uni_db).
How To Read This Notebook¶
- Define schema first, then load data.
- Keep Locy rules declarative and focused.
- Read output rows together with materialization stats.
1) Setup¶
Initialize an in-memory database and import DataType for schema definitions.
2) Define Schema (Recommended)¶
Define labels, typed properties, and edge types before inserting graph facts.
db.schema()
.label("User")
.property("name", DataType::String)
.label("Resource")
.property("name", DataType::String)
.edge_type("ALLOWED", &["User"], &["Resource"])
.edge_type("DENIED", &["User"], &["Resource"])
.apply()
.await?;
println!("Schema created");
3) Seed Graph Data¶
Insert the minimal graph needed for the scenario.
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:User {name: 'alice'})").await?;
tx.execute("CREATE (:User {name: 'bob'})").await?;
tx.execute("CREATE (:Resource {name: 'prod-db'})").await?;
tx.execute("MATCH (u:User {name:'alice'}), (r:Resource {name:'prod-db'}) CREATE (u)-[:ALLOWED]->(r)").await?;
tx.execute("MATCH (u:User {name:'bob'}), (r:Resource {name:'prod-db'}) CREATE (u)-[:ALLOWED]->(r)").await?;
tx.execute("MATCH (u:User {name:'bob'}), (r:Resource {name:'prod-db'}) CREATE (u)-[:DENIED]->(r)").await?;
tx.commit().await?;
println!("Seeded graph data");
4) Locy Program¶
Rules derive relations, then QUERY ... WHERE ... RETURN ... projects the final answer.
let program = r#"CREATE RULE access PRIORITY 1 AS\nMATCH (u:User)-[:ALLOWED]->(r:Resource)\nYIELD KEY u, KEY r, 1 AS decision_code\n\nCREATE RULE access PRIORITY 2 AS\nMATCH (u:User)-[:DENIED]->(r:Resource)\nYIELD KEY u, KEY r, 2 AS decision_code"#;
5) Evaluate¶
Evaluate the Locy program and inspect stats/rows.
let session = db.session();
let result = session.locy(program).await?;
println!("Derived relations: {:?}", result.derived.keys().collect::<Vec<_>>());
println!("Iterations: {}", result.stats().total_iterations);
println!("Queries executed: {}", result.stats().queries_executed);
for (name, rows) in &result.derived {
println!("{}: {} row(s)", name, rows.len());
}
if let Some(rows) = result.rows() {
println!("Rows: {:?}", rows);
}
6) What To Expect¶
Use these checks to validate output after evaluation:
- In derived['access'], alice should have decision_code = 1 (ALLOW path).
- In derived['access'], bob should have decision_code = 2 (DENY override).
- Exactly one derived row per (user, resource) should remain after priority filtering.
Notes¶
- Rust notebooks are included for API parity and learning.
- In this docs build, Rust notebooks are rendered without execution.