1use aauth_protocol::{Budget, Jwk};
2use serde::{Deserialize, Serialize};
3use serde_json::{Map, Value};
4
5#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
7pub struct Person {
8 pub id: String,
11 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub name: Option<String>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub email: Option<String>,
17 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub tenant: Option<String>,
20 #[serde(flatten)]
22 pub extra: Map<String, Value>,
23}
24
25impl Person {
26 pub fn new(id: impl Into<String>) -> Self {
28 Self {
29 id: id.into(),
30 ..Default::default()
31 }
32 }
33}
34
35#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
37pub struct Agent {
38 pub id: String,
40 pub provider: String,
42 pub jkt: String,
44 pub jwk: Jwk,
46 pub exp: i64,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53#[non_exhaustive]
54pub enum ApprovalKind {
55 Mission,
57 AuthToken,
59 Completion,
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "snake_case")]
66#[non_exhaustive]
67pub enum ApprovalStatus {
68 Pending,
70 Approved,
73 Denied,
75 Collected,
78}
79
80#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
82pub struct Approval {
83 pub id: String,
85 pub kind: ApprovalKind,
87 pub status: ApprovalStatus,
89 pub agent: Agent,
91 pub person: Person,
93 pub approver: Person,
95 pub description: String,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub summary: Option<String>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub mission_s256: Option<String>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub resource: Option<String>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub budget: Option<Budget>,
109 #[serde(default)]
111 pub grant: Map<String, Value>,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub decided_budget: Option<Budget>,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub decided_by: Option<String>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub decided_at: Option<i64>,
121 pub created_at: i64,
123 pub expires_at: i64,
125 pub tenant: String,
127}
128
129#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
131pub struct LogEntry {
132 pub at: i64,
134 pub entry: String,
136}
137
138#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
140pub struct Mission {
141 pub s256: String,
143 pub mission: String,
145 pub agent_id: String,
147 pub person: Person,
149 pub resources: Vec<String>,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub budget: Option<Budget>,
154 pub spent: u64,
156 #[serde(default)]
158 pub log: Vec<LogEntry>,
159 pub expires_at: i64,
161 #[serde(default, skip_serializing_if = "Option::is_none")]
163 pub completed_at: Option<i64>,
164 pub tenant: String,
166}
167
168impl Mission {
169 pub fn is_live(&self, now: i64) -> bool {
171 self.completed_at.is_none() && now < self.expires_at
172 }
173}
174
175#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
177#[serde(rename_all = "snake_case")]
178pub enum Verdict {
179 Approved,
181 Denied,
183}
184
185#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
187pub struct Decision {
188 pub status: Verdict,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
192 pub budget: Option<Budget>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub decided_by: Option<String>,
196}
197
198impl Decision {
199 pub fn approve() -> Self {
201 Self {
202 status: Verdict::Approved,
203 budget: None,
204 decided_by: None,
205 }
206 }
207
208 pub fn deny() -> Self {
210 Self {
211 status: Verdict::Denied,
212 budget: None,
213 decided_by: None,
214 }
215 }
216
217 pub fn with_budget(mut self, budget: Budget) -> Self {
219 self.budget = Some(budget);
220 self
221 }
222
223 pub fn by(mut self, decided_by: impl Into<String>) -> Self {
225 self.decided_by = Some(decided_by.into());
226 self
227 }
228}
229
230#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
232#[serde(rename_all = "snake_case")]
233#[non_exhaustive]
234pub enum IssuedKind {
235 PersonToken,
237 AuthToken,
239}
240
241#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
243pub struct Issued {
244 pub kind: IssuedKind,
246 pub agent: Agent,
248 pub person: Person,
250 pub resource: String,
252 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub mission: Option<Mission>,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub budget: Option<Budget>,
258}