feat(core,call): implement ADR-050 dynamic resource ownership — ownership store, resource_id_path, check signature, dispatch wiring

Implements the 4-task DAG for runtime-spawned resource ownership so
AccessControl::check can answer "does this identity own this specific
container/TTY/process" instead of relying only on static
Identity.resources grants.

1. core/ownership-store-trait: OwnershipProvider (sync read) +
   OwnershipStore (async write) traits + InMemoryOwnershipStore +
   OwnershipError in alknet-core. Fourth instance of the repo/adapter
   pattern (ADR-033), mirroring CredentialStore/store.rs.

2. call/registry/operation-spec-resource-id-path: resource_id_path
   field on OperationSpec — JSON pointer into input for resource ID
   extraction. Single field addition, all ~40 construction sites across
   alknet-call + alknet-http updated to pass None (no semantic change).

3. call/registry/access-control-ownership-check: check() signature
   gains resource_id + Option<&dyn OwnershipProvider>. 3-case decision
   tree: ownership Some + resource_id Some -> owns(); ownership Some +
   resource_id None -> owns_any() (list scope-gate); ownership None ->
   static Identity.resources fallback (backward compat). 7 call sites
   updated to (None, None) — including a 7th in alknet-http/gateway_routes
   not listed in the original spec.

4. call/registry/dispatch-resource-id-extraction: wire the dispatch
   path. OperationContext gains ownership field; Dispatcher/CallAdapter
   gain with_ownership_provider builders; invoke/invoke_streaming/
   invoke_with_policy extract resource_id via spec.resource_id_path
   and thread context.ownership to check(). extract_json_pointer helper
   handles $.field syntax (graceful None on missing/non-string). 20
   OperationContext literals updated across both crates.

Backward compatibility is load-bearing throughout: ownership=None
falls back to the existing static resource-check path. Deployments
without runtime-spawned resources wire nothing and behave identically.

821 tests pass workspace-wide (was ~770); clippy clean; fmt clean.
Task specs marked done.
This commit is contained in:
2026-07-05 12:57:33 +00:00
parent de536b82e1
commit 73724b8a1d
28 changed files with 1000 additions and 29 deletions

View File

@@ -91,6 +91,7 @@ mod tests {
serde_json::json!({}),
vec![],
acl,
None,
)
}
@@ -103,6 +104,7 @@ mod tests {
serde_json::json!({}),
vec![],
AccessControl::default(),
None,
)
}
@@ -170,6 +172,7 @@ mod tests {
abort_policy: AbortPolicy::default(),
deadline: Some(Instant::now() + Duration::from_secs(30)),
internal: true,
ownership: None,
}
}