1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Authorization: who may do what to which resource.
//!
//! # What this is not
//!
//! It is not the information-flow lattice. Labels answer *may this value go
//! there* — a sensitivity ceiling on a sink, a taint gate on a mutation — and
//! they travel with the data. Policy answers *may this principal do this at
//! all*, and it travels with the request. Both gates exist because either one
//! alone leaves a hole: a correctly-labelled value sent by someone with no
//! authority, or an authorized caller exfiltrating a secret through a sink that
//! looks innocuous.
//!
//! # Evaluation is total and side-effect free
//!
//! [`PolicyEngine::authorize`] is synchronous and returns a [`PolicyDecision`], not a
//! `Result`. There is deliberately no way to express "the policy service was
//! unreachable", because a runtime that can fail *open* under load has no policy
//! layer — it has a policy layer that turns itself off exactly when a system is
//! under stress, which is when authorization matters most.
//!
//! This is the constraint that points at an embedded evaluator over a network
//! call: a policy set loaded into the process, evaluated against a request, with
//! no I/O in the path. Cedar is the obvious fit and this trait is shaped for it —
//! `principal`, `action`, `resource`, `context` is Cedar's vocabulary — but the
//! crate ships no engine. Picking one for the embedder would be the same mistake
//! as picking their tracing exporter.
//!
//! # Determinism, and why decisions are not journaled wholesale
//!
//! A policy decision made inside a run is a non-deterministic input in exactly
//! the sense the rest of this crate means it: the answer depends on a policy set
//! that can change between the run and its replay. The naive fixes are both
//! wrong. Journaling every permit doubles the journal to record "yes" over and
//! over. Re-evaluating on replay means a policy edit silently rewrites history —
//! last year's run is re-judged under this year's rules, and the audit trail
//! quietly becomes a lie.
//!
//! The answer is the one the effect protocol already gives, applied unchanged:
//!
//! > **Policy is evaluated only when an effect is actually dispatched.**
//!
//! A replayed effect never reaches the gate, because it never reaches the world
//! — its result comes back from the journal. So a permit needs no record: the
//! effect's own `EffectDone` *is* the record that it was allowed. What does need
//! a record is a **denial**, because a denial is a place the run stopped, and a
//! stop with no record replays as "this build performs more effects than the
//! recorded one". That is precisely why `BudgetRefused` exists, and
//! `PolicyDenied` is its twin.
//!
//! What is journaled once, at admission, is the **policy digest** — which rules
//! governed this run. That is an audit question (§17), not a replay one, and it
//! makes "the policy changed" visible without making it fatal.
use Debug;
use ;
use Value;
use crateDigest;
/// What is being asked.
///
/// Borrowed rather than owned: this is built at every effect dispatch, and a
/// gate that allocates four strings per call is a gate people turn off.
/// The answer.
///
/// Not a `Result`, on purpose: there is no error case. See the module docs on
/// why a policy layer that can fail open is not a policy layer.
/// Decides whether an action is allowed.
///
/// Implementations must be **total** — every request gets an answer — and
/// **pure**: no I/O, no clock, no randomness. Two calls with the same request
/// against the same policy set must return the same decision, or a run stops
/// being replayable for reasons nobody can see.
/// Refuses everything, naming itself.
///
/// Exists for tests and as the thing to reach for when wiring a policy layer
/// before its rules are written: starting closed and opening deliberately is the
/// order that fails safe. There is deliberately **no** `AllowAll` counterpart —
/// a permissive engine and no engine at all are the same behaviour, and having
/// two ways to spell it is how a plane ends up with a policy layer that
/// everybody believes is switched on.
;
/// The action string for performing an effect.
pub const ACTION_PERFORM: &str = "effect:perform";
/// The action string for starting a run.
pub const ACTION_ADMIT: &str = "run:admit";