98 lines
3.7 KiB
Markdown
98 lines
3.7 KiB
Markdown
# Policy Decision And Provenance Contract
|
|
|
|
`govoplan-policy` owns policy and retention route contributions. Core keeps the
|
|
small shared DTOs that let policy decisions look the same across modules.
|
|
|
|
Privacy retention implementation lives in this module at
|
|
`govoplan_policy.backend.retention`. It is also exposed as the
|
|
`policy.privacyRetention` capability so older core compatibility imports can
|
|
dispatch to the active policy module without owning policy logic in core. Core
|
|
does not import this implementation as a hidden fallback when policy is
|
|
disabled.
|
|
|
|
Reusable hierarchical policy validation lives in
|
|
`govoplan_policy.backend.hierarchy`. Policy families should use that helper for
|
|
parent locks, lower-level override ceilings, "more restrictive only" checks,
|
|
and read-only simulations before destructive or limiting changes are saved.
|
|
Domain modules keep their own policy fields and restriction rules, but the
|
|
decision shape and simulation payload stay consistent.
|
|
When retention needs audit-log storage behavior, it requests the
|
|
`audit.retention` capability; it does not import audit module tables or
|
|
providers directly.
|
|
|
|
## Backend DTOs
|
|
|
|
Use `govoplan_core.core.policy.PolicyDecision` for explainable policy results:
|
|
|
|
- `allowed`: effective decision for the checked action.
|
|
- `reason`: compact operator-readable explanation.
|
|
- `source_path`: ordered policy sources that produced the decision.
|
|
- `requirements`: machine-readable blockers or prerequisites.
|
|
- `details`: domain-specific structured context, redacted when needed.
|
|
|
|
Use `PolicySourceStep` or `policy_source_step()` for each provenance step:
|
|
|
|
- `scope_type`: `system`, `tenant`, `user`, `group`, or `campaign`.
|
|
- `scope_id`: stable ID for non-system scopes.
|
|
- `path`: stable string path generated by `policy_source_path()`.
|
|
- `label`: concrete source label such as `System`, `Tenant`, `Owner user`,
|
|
`Group`, or `Campaign`.
|
|
- `applied_fields`: field names affected by that step.
|
|
- `policy`: local policy fragment that explains the applied fields.
|
|
|
|
Do not build or split provenance paths manually. Use
|
|
`policy_source_path()` and `parse_policy_source_path()` so IDs are URL-encoded
|
|
consistently.
|
|
|
|
## Retention Explain Endpoint
|
|
|
|
Retention policy exposes the shared shape through:
|
|
|
|
```text
|
|
GET /api/v1/admin/privacy-retention/policies/{scope_type}/explain
|
|
```
|
|
|
|
The response contains `decision`, `effective_policy`, `parent_policy`,
|
|
`effective_policy_sources`, `parent_policy_sources`, and `blocked_fields`.
|
|
|
|
Retention policy also exposes a write-preflight endpoint:
|
|
|
|
```text
|
|
POST /api/v1/admin/privacy-retention/policies/{scope}/simulate
|
|
```
|
|
|
|
The request body is the same as the write endpoint. The response contains a
|
|
`simulation` object with `allowed`, `changed_fields`, `issues`,
|
|
`before_policy`, `requested_policy`, and a shared `PolicyDecision` payload.
|
|
The endpoint never writes policy state and is intended for UI validation before
|
|
operators attempt destructive or limiting changes.
|
|
Clients can use `blocked_fields` to disable controls before a save attempt.
|
|
|
|
## UI Expectations
|
|
|
|
Policy UIs should render provenance close to the effective column or field it
|
|
explains. The display path should use concrete source labels and local values,
|
|
for example:
|
|
|
|
```text
|
|
System: Allow
|
|
> Tenant: Deny without override
|
|
```
|
|
|
|
If all lower levels still inherit, continue the path until the effective local
|
|
decision:
|
|
|
|
```text
|
|
System: Allow
|
|
> Tenant: Inherit
|
|
> Group: Inherit
|
|
> Campaign: Deny
|
|
```
|
|
|
|
When a parent disallows lower-level limits or changes, the UI should disable
|
|
the affected controls and avoid sending those fields in the save payload.
|
|
|
|
The shared core WebUI helper `PolicySourcePath` renders the source path shape
|
|
for module UIs. Modules may use their own field layout, but the data contract
|
|
should remain this shape.
|