JanssenProject / JanssenProject/jans

feat(jans-cedarling): expose current policy store ID

Open
#14,842 0 comments 0 reactions 3 assignees Claimed by @haileyesus2433 View on GitHub
comp-jans-cedarling kind-feature
Dominant language
Java
Stars
647
Forks
173
Avg merge
1d 18h
Merged PRs (30d)
110

Description

# Feature: expose current policy store ID

### Problem

Cedarling knows the ID of the policy store it loaded (`PolicyStoreWithID.id`, `jans-cedarling/cedarling/src/common/policy_store.rs:111`), but the type is `pub(crate)`. The ID is only visible indirectly, inside decision log entries (`policystore_id`, `cedarling/src/authz/mod.rs:1120`).

Library and binding users have no way to answer "which policy store is this instance running right now?" — needed for diagnostics, multi-tenant routing, and confirming a background refresh actually picked up a new store.

### Proposal

Public getter returning the ID of the **currently active** store, `None`/null when the store carries no ID.

- Core: `Cedarling::policy_store_id(&self) -> Option` in `cedarling/src/lib.rs`, backed by `Authz::policy_store_id()` reading `config.policy_store.id`. Mirror in `cedarling/src/blocking.rs`. **Not** gated behind the `tools` feature.
- Bindings (all of them, same semantics):
- uniffi (Kotlin/Swift/Java): `Option`
- WASM: `Option` → `string | undefined`
- Python: `Optional[str]` + `.pyi` stub
- Go: bridge returns `String` (`""` = unset, rust2go has no `Option`); Go wrapper `PolicyStoreID() (string, bool)`. Requires `internal/gen.go` regen + `.so` rebuild.
- C: `cedarling_get_policy_store_id(u64 instance_id, char** out_id) -> c_int`, `*out_id = NULL` **with `Success`** when unset (plain `NULL` return would be ambiguous with error). Free via `cedarling_free_string`. Header regen.

### Pitfalls to handle

1. **ID format differs per source.** New format (`.cjar`/directory) → hex 8–64 chars from `metadata.json`, but the field is `#[serde(default)]` and can legitimately be `""`. Legacy Agama JSON/YAML/Lock-Server → ID is the **map key** of `policy_stores`, not hex. Document as an opaque, source-dependent string.
2. **Two ID fields disagree.** Legacy path (`init/policy_store.rs:91`) sets `PolicyStoreWithID.id = key` but leaves `metadata.policy_store.id = String::new()`. Fix: populate metadata ID from the same key. `PolicyStoreWithID.id` stays the single source of truth (it is what the log entry uses).
3. **Store is swapped at runtime.** `Cedarling.authz` is an `ArcSwap` and the refresh worker publishes a rebuilt `Authz`. The getter must read through `authz.load()` on every call — never cache at construction. The ID read after `authorize()` may already be a different one; for per-decision correlation the log entry's `policystore_id` is authoritative. Hot reload to a different ID: just report the new ID, no warn, no rejection.
4. **Don't make `PolicyStoreWithID` / `PolicyStoreMetadata` public** just for this — `Option` keeps the public type surface unchanged.

### Tests

- `cedarling/src/tests/policy_store_loader.rs`: ID present for `.cjar` + directory; ID equals `policy_stores` key for Agama JSON/YAML; `None` when `metadata.json` omits `id`.
- `cedarling/src/init/policy_store_refresh.rs` tests: after a swap to a store with a different ID, getter returns the **new** ID.
- Negative: new-format store with malformed `id` fails with `ValidationError::InvalidPolicyStoreId` specifically.
- Per-binding smoke tests next to the existing `total_issuers` tests.

### Docs

- `docs/cedarling/reference/cedarling-interfaces.md` — add to each language's interface table.
- `docs/cedarling/reference/cedarling-policy-store.md` — ID semantics per source format + hot-reload caveat.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.