mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-21 02:28:07 +02:00
fix(config): enforce graph-scoped policies and query validation
This commit is contained in:
parent
fb442adb14
commit
845e32324c
12 changed files with 682 additions and 168 deletions
|
|
@ -74,14 +74,14 @@ project:
|
|||
graphs:
|
||||
local:
|
||||
uri: {}
|
||||
policy:
|
||||
file: ./policy.yaml
|
||||
cli:
|
||||
graph: local
|
||||
branch: main
|
||||
query:
|
||||
roots:
|
||||
- .
|
||||
policy:
|
||||
file: ./policy.yaml
|
||||
",
|
||||
yaml_string(&graph.path().to_string_lossy())
|
||||
)
|
||||
|
|
@ -1000,8 +1000,8 @@ query vector_search($q: String) {
|
|||
#[test]
|
||||
fn local_cli_policy_tooling_is_end_to_end() {
|
||||
// Sanity check for the read-only policy CLI surfaces. These don't
|
||||
// mutate the graph — they just parse and evaluate the policy file —
|
||||
// so they don't depend on PR #4's engine-side enforcement.
|
||||
// mutate the graph; they parse and evaluate the effective policy for
|
||||
// the `cli.graph` selection, including per-graph policy files.
|
||||
let graph = SystemGraph::loaded();
|
||||
let config = graph.write_config("omnigraph-policy.yaml", &local_policy_config(&graph));
|
||||
graph.write_config("policy.yaml", POLICY_E2E_YAML);
|
||||
|
|
@ -1039,10 +1039,10 @@ fn local_cli_policy_tooling_is_end_to_end() {
|
|||
|
||||
#[test]
|
||||
fn local_cli_change_enforces_engine_layer_policy() {
|
||||
// Asserts MR-722 PR #4: when `policy.file` is configured in
|
||||
// `omnigraph.yaml`, the CLI loads PolicyEngine into Omnigraph and
|
||||
// every direct-engine write hits `enforce(action, scope, actor)` —
|
||||
// identical to what the HTTP server gets, regardless of transport.
|
||||
// Asserts MR-722 PR #4: when the selected graph has a configured
|
||||
// policy file, the CLI loads PolicyEngine into Omnigraph and every
|
||||
// direct-engine write hits `enforce(action, scope, actor)` — identical
|
||||
// to what the HTTP server gets, regardless of transport.
|
||||
//
|
||||
// Three cases, each discriminating:
|
||||
//
|
||||
|
|
@ -1135,6 +1135,32 @@ fn local_cli_change_enforces_engine_layer_policy() {
|
|||
assert_eq!(verify["rows"][0]["p.name"], "RagnorOnMain");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_cli_positional_uri_does_not_inherit_default_graph_policy() {
|
||||
let graph = SystemGraph::loaded();
|
||||
let config = graph.write_config("omnigraph-policy.yaml", &local_policy_config(&graph));
|
||||
graph.write_config("policy.yaml", POLICY_E2E_YAML);
|
||||
let mutation_file = insert_person_query(&graph, "system-local-policy-positional.gq");
|
||||
|
||||
let allowed = parse_stdout_json(&output_success(
|
||||
cli()
|
||||
.arg("--as")
|
||||
.arg("act-bruno")
|
||||
.arg("change")
|
||||
.arg("--config")
|
||||
.arg(&config)
|
||||
.arg("--uri")
|
||||
.arg(graph.path())
|
||||
.arg("--query")
|
||||
.arg(&mutation_file)
|
||||
.arg("--params")
|
||||
.arg(r#"{"name":"PositionalUriBruno","age":4}"#)
|
||||
.arg("--json"),
|
||||
));
|
||||
assert_eq!(allowed["affected_nodes"], 1);
|
||||
assert_eq!(allowed["actor_id"], "act-bruno");
|
||||
}
|
||||
|
||||
// ─── MR-722 PR A: CLI×writer matrix ───────────────────────────────────────
|
||||
//
|
||||
// The change writer is covered above by `local_cli_change_enforces_engine_layer_policy`.
|
||||
|
|
@ -1293,6 +1319,62 @@ fn local_cli_schema_apply_enforces_engine_layer_policy() {
|
|||
assert_eq!(allowed["applied"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_cli_schema_apply_rejects_stored_query_breakage_before_publish() {
|
||||
let graph = SystemGraph::loaded();
|
||||
graph.write_query(
|
||||
"stored-find-person.gq",
|
||||
"query find_person($name: String) { match { $p: Person { name: $name } } return { $p.age } }",
|
||||
);
|
||||
let config = graph.write_config(
|
||||
"omnigraph-stored-query-schema.yaml",
|
||||
&format!(
|
||||
"\
|
||||
graphs:
|
||||
local:
|
||||
uri: {}
|
||||
queries:
|
||||
find_person:
|
||||
file: ./stored-find-person.gq
|
||||
cli:
|
||||
graph: local
|
||||
branch: main
|
||||
query:
|
||||
roots:
|
||||
- .
|
||||
policy: {{}}
|
||||
",
|
||||
yaml_string(&graph.path().to_string_lossy())
|
||||
),
|
||||
);
|
||||
let renamed_schema = std::fs::read_to_string(fixture("test.pg"))
|
||||
.unwrap()
|
||||
.replace("age: I32?", "years: I32? @rename_from(\"age\")");
|
||||
let schema_path = graph.write_file("stored-query-breaks.pg", &renamed_schema);
|
||||
|
||||
let rejected = output_failure(
|
||||
cli()
|
||||
.arg("schema")
|
||||
.arg("apply")
|
||||
.arg("--config")
|
||||
.arg(&config)
|
||||
.arg("--schema")
|
||||
.arg(&schema_path)
|
||||
.arg("--json"),
|
||||
);
|
||||
let stderr = String::from_utf8_lossy(&rejected.stderr);
|
||||
assert!(
|
||||
stderr.contains("find_person") && stderr.contains("schema check"),
|
||||
"schema apply should reject the stored-query breakage before publish; stderr: {stderr}"
|
||||
);
|
||||
|
||||
let schema = stdout_string(&output_success(
|
||||
cli().arg("schema").arg("show").arg("--config").arg(&config),
|
||||
));
|
||||
assert!(schema.contains("age: I32?"));
|
||||
assert!(!schema.contains("years: I32?"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_cli_branch_create_enforces_engine_layer_policy() {
|
||||
let graph = SystemGraph::loaded();
|
||||
|
|
@ -1448,6 +1530,8 @@ project:
|
|||
graphs:
|
||||
local:
|
||||
uri: {}
|
||||
policy:
|
||||
file: ./policy.yaml
|
||||
cli:
|
||||
graph: local
|
||||
branch: main
|
||||
|
|
@ -1455,8 +1539,6 @@ cli:
|
|||
query:
|
||||
roots:
|
||||
- .
|
||||
policy:
|
||||
file: ./policy.yaml
|
||||
",
|
||||
yaml_string(&graph.path().to_string_lossy()),
|
||||
actor,
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ project:
|
|||
graphs:
|
||||
local:
|
||||
uri: {}
|
||||
policy:
|
||||
file: ./policy.yaml
|
||||
server:
|
||||
graph: local
|
||||
policy:
|
||||
file: ./policy.yaml
|
||||
",
|
||||
yaml_string(&graph.path().to_string_lossy())
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue