1use chrono::{DateTime, Utc};
2use serde::de::Deserializer;
3use serde::ser::Serializer;
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6use std::fmt::Display;
7
8use crate::AgentResult;
9
10#[derive(Debug, PartialEq, Hash, Eq, Clone)]
11pub enum TokenInfoPerms {
12 Introspect,
14 Subtokens,
16 History,
18 All,
20}
21
22impl Display for TokenInfoPerms {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 match *self {
25 Self::Introspect => write!(f, "tokeninfo:introspect"),
26 Self::Subtokens => write!(f, "tokeninfo:subtokens"),
27 Self::History => write!(f, "tokeninfo:history"),
28 Self::All => write!(f, "tokeninfo"),
29 }
30 }
31}
32
33#[derive(Debug, PartialEq, Hash, Eq, Clone)]
34pub enum MgmtPerms {
35 List,
37 Revoke,
39 History,
41 All,
43}
44
45impl Display for MgmtPerms {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match *self {
48 Self::List => write!(f, "manage_mytoken:list"),
49 Self::Revoke => write!(f, "manage_mytoken:revoke"),
50 Self::History => write!(f, "manage_mytoken:history"),
51 Self::All => write!(f, "manage_mytoken"),
52 }
53 }
54}
55
56#[derive(Debug, Hash, PartialEq, Eq, Clone)]
57pub enum SettingsPerms {
58 Ssh,
60 Grants,
62 All,
64 ReadSsh,
66 ReadGrants,
68 ReadAll,
70}
71
72impl Display for SettingsPerms {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match *self {
75 Self::Ssh => write!(f, "settings:grants:ssh"),
76 Self::Grants => write!(f, "settings:grants"),
77 Self::All => write!(f, "settings"),
78 Self::ReadSsh => write!(f, "read@settings:grants:ssh"),
79 Self::ReadGrants => write!(f, "read@settings:grants"),
80 Self::ReadAll => write!(f, "read@settings"),
81 }
82 }
83}
84
85#[derive(Hash, PartialEq, Eq, Debug, Clone)]
86pub enum Capability {
87 AT,
88 TokenInfo(TokenInfoPerms),
89 MyTokenMgmt(MgmtPerms),
90 MyTokenCreate,
91 Settings(SettingsPerms),
92}
93
94impl Serialize for Capability {
95 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96 where
97 S: Serializer,
98 {
99 match *self {
100 Capability::AT => serializer.serialize_str("AT"),
101 Capability::TokenInfo(ref perm) => serializer.serialize_str(&perm.to_string()),
102 Capability::MyTokenMgmt(ref perm) => serializer.serialize_str(&perm.to_string()),
103 Capability::MyTokenCreate => serializer.serialize_str("create_mytoken"),
104 Capability::Settings(ref perm) => serializer.serialize_str(&perm.to_string()),
105 }
106 }
107}
108
109impl<'de> Deserialize<'de> for Capability {
110 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111 where
112 D: Deserializer<'de>,
113 {
114 let s = String::deserialize(deserializer)?;
115 match s.as_str() {
116 "AT" => Ok(Capability::AT),
117 "tokeninfo" => Ok(Capability::TokenInfo(TokenInfoPerms::All)),
118 "tokeninfo:introspect" => Ok(Capability::TokenInfo(TokenInfoPerms::Introspect)),
119 "tokeninfo:subtokens" => Ok(Capability::TokenInfo(TokenInfoPerms::Subtokens)),
120 "tokeninfo:history" => Ok(Capability::TokenInfo(TokenInfoPerms::History)),
121 "manage_mytoken" => Ok(Capability::MyTokenMgmt(MgmtPerms::All)),
122 "manage_mytoken:list" => Ok(Capability::MyTokenMgmt(MgmtPerms::List)),
123 "manage_mytoken:revoke" => Ok(Capability::MyTokenMgmt(MgmtPerms::Revoke)),
124 "manage_mytoken:history" => Ok(Capability::MyTokenMgmt(MgmtPerms::History)),
125 "create_mytoken" => Ok(Capability::MyTokenCreate),
126 "settings" => Ok(Capability::Settings(SettingsPerms::All)),
127 "settings:grants" => Ok(Capability::Settings(SettingsPerms::Grants)),
128 "settings:grants:ssh" => Ok(Capability::Settings(SettingsPerms::Ssh)),
129 "read@settings" => Ok(Capability::Settings(SettingsPerms::ReadAll)),
130 "read@settings:grants" => Ok(Capability::Settings(SettingsPerms::ReadGrants)),
131 "read@settings:grants:ssh" => Ok(Capability::Settings(SettingsPerms::ReadSsh)),
132 _ => Err(serde::de::Error::custom("Invalid capability!")),
133 }
134 }
135}
136
137#[derive(Serialize, Deserialize, Debug)]
138#[serde(rename_all = "lowercase")]
139#[allow(non_camel_case_types)]
140pub enum MyTokenType {
141 TOKEN,
142 SHORT_TOKEN,
143 TRANSER_CODE,
144}
145
146#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
147#[allow(non_snake_case)]
148pub struct Restriction {
149 #[serde(default, with = "chrono::serde::ts_seconds_option")]
150 #[serde(skip_serializing_if = "Option::is_none")]
151 nbf: Option<DateTime<Utc>>,
152
153 #[serde(default, with = "chrono::serde::ts_seconds_option")]
154 #[serde(skip_serializing_if = "Option::is_none")]
155 exp: Option<DateTime<Utc>>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
158 scope: Option<String>,
159
160 #[serde(skip_serializing_if = "Option::is_none")]
161 audience: Option<Vec<String>>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
164 ip: Option<Vec<String>>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
167 geoip_allow: Option<Vec<String>>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
170 geoip_disallow: Option<Vec<String>>,
171
172 #[serde(skip_serializing_if = "Option::is_none")]
173 usages_AT: Option<u64>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
176 usages_other: Option<u64>,
177}
178
179impl Default for Restriction {
180 fn default() -> Self {
181 Self {
182 nbf: None,
183 exp: None,
184 scope: None,
185 audience: None,
186 ip: None,
187 geoip_allow: None,
188 geoip_disallow: None,
189 usages_AT: None,
190 usages_other: None,
191 }
192 }
193}
194
195#[allow(non_snake_case)]
196impl Restriction {
197 pub fn new() -> Self {
198 Restriction::default()
199 }
200 pub fn set_nbf(&mut self, nbf: DateTime<Utc>) {
201 self.nbf = Some(nbf)
202 }
203 pub fn set_exp(&mut self, exp: DateTime<Utc>) {
204 self.exp = Some(exp)
205 }
206 pub fn add_scope<T: ToString>(&mut self, scope: T) {
207 if let Some(ref mut curr_scope) = self.scope {
208 curr_scope.push_str(" ");
209 curr_scope.push_str(&scope.to_string());
210 } else {
211 self.scope = Some(scope.to_string().trim().to_string());
212 }
213 }
214 pub fn add_audiences<I, T>(&mut self, audiences: I)
215 where
216 I: IntoIterator<Item = T>,
217 T: ToString,
218 {
219 let audiences = audiences
220 .into_iter()
221 .map(|s| s.to_string())
222 .collect::<HashSet<_>>();
223 self.audience.get_or_insert_with(Vec::new).extend(audiences)
224 }
225 pub fn add_ips<I, T>(&mut self, hosts: I)
226 where
227 I: IntoIterator<Item = T>,
228 T: ToString,
229 {
230 let hosts = hosts
231 .into_iter()
232 .map(|s| s.to_string())
233 .collect::<HashSet<_>>();
234 self.ip.get_or_insert_with(Vec::new).extend(hosts);
235 }
236 pub fn add_geoip_allow<I, T>(&mut self, geoip_allow: I)
237 where
238 I: IntoIterator<Item = T>,
239 T: ToString,
240 {
241 let geoip_allow = geoip_allow
242 .into_iter()
243 .map(|s| s.to_string())
244 .collect::<HashSet<_>>();
245 self.geoip_allow
246 .get_or_insert_with(Vec::new)
247 .extend(geoip_allow);
248 }
249 pub fn add_geoip_disallow<I, T>(&mut self, geoip_disallow: I)
250 where
251 I: IntoIterator<Item = T>,
252 T: ToString,
253 {
254 let geoip_disallow = geoip_disallow
255 .into_iter()
256 .map(|s| s.to_string())
257 .collect::<HashSet<_>>();
258 self.geoip_disallow
259 .get_or_insert_with(Vec::new)
260 .extend(geoip_disallow);
261 }
262 pub fn set_usage_AT(&mut self, n: u64) {
263 self.usages_AT = Some(n);
264 }
265 pub fn set_usage_other(&mut self, n: u64) {
266 self.usages_other = Some(n);
267 }
268 pub fn builder() -> RestrictionBuilder {
269 RestrictionBuilder(Restriction::default())
270 }
271}
272
273#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
274#[allow(non_snake_case)]
275pub struct Rotation {
276 #[serde(skip_serializing_if = "Option::is_none")]
277 on_AT: Option<bool>,
278
279 #[serde(skip_serializing_if = "Option::is_none")]
280 on_other: Option<bool>,
281
282 #[serde(skip_serializing_if = "Option::is_none")]
283 lifetime: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
286 auto_revoke: Option<bool>,
287}
288
289impl Rotation {
290 pub fn builder() -> RotationBuilder {
291 RotationBuilder(Self {
292 on_AT: None,
293 on_other: None,
294 lifetime: None,
295 auto_revoke: None,
296 })
297 }
298}
299
300pub struct RotationBuilder(Rotation);
301
302#[allow(non_snake_case)]
303impl RotationBuilder {
304 pub fn set_on_AT(mut self) -> Self {
305 self.0.on_AT = Some(true);
306 self
307 }
308 pub fn unset_on_AT(mut self) -> Self {
309 self.0.on_AT = Some(false);
310 self
311 }
312 pub fn set_on_other(mut self) -> Self {
313 self.0.on_other = Some(true);
314 self
315 }
316 pub fn unset_on_other(mut self) -> Self {
317 self.0.on_other = Some(false);
318 self
319 }
320 pub fn set_lifetime(mut self, lifetime: u64) -> Self {
321 self.0.lifetime = Some(lifetime);
322 self
323 }
324 pub fn set_auto_revoke(mut self) -> Self {
325 self.0.auto_revoke = Some(true);
326 self
327 }
328 pub fn unset_auto_revoke(mut self) -> Self {
329 self.0.auto_revoke = Some(false);
330 self
331 }
332 pub fn build(self) -> AgentResult<Rotation> {
333 if self.0.on_AT == Some(true) || self.0.on_other == Some(true) {
334 return Ok(self.0);
335 }
336 Err("Failed to build rotation object! on_AT or on_other must be set!".into())
337 }
338}
339
340pub struct RestrictionBuilder(Restriction);
341
342#[allow(non_snake_case)]
343impl RestrictionBuilder {
344 pub fn nbf(mut self, nbf: DateTime<Utc>) -> Self {
345 self.0.set_nbf(nbf);
346 self
347 }
348 pub fn exp(mut self, exp: DateTime<Utc>) -> Self {
349 self.0.set_exp(exp);
350 self
351 }
352 pub fn add_scope<T: ToString>(mut self, scope: T) -> Self {
353 self.0.add_scope(scope);
354 self
355 }
356 pub fn add_audiences<I, T>(mut self, audiences: I) -> Self
357 where
358 I: IntoIterator<Item = T>,
359 T: ToString,
360 {
361 self.0.add_audiences(audiences);
362 self
363 }
364 pub fn add_ips<I, T>(mut self, hosts: I) -> Self
365 where
366 I: IntoIterator<Item = T>,
367 T: ToString,
368 {
369 self.0.add_ips(hosts);
370 self
371 }
372 pub fn add_geoip_allow<I, T>(mut self, geoip_allow: I) -> Self
373 where
374 I: IntoIterator<Item = T>,
375 T: ToString,
376 {
377 self.0.add_geoip_allow(geoip_allow);
378 self
379 }
380 pub fn add_geoip_disallow<I, T>(mut self, geoip_disallow: I) -> Self
381 where
382 I: IntoIterator<Item = T>,
383 T: ToString,
384 {
385 self.0.add_geoip_disallow(geoip_disallow);
386 self
387 }
388 pub fn usages_AT(mut self, n: u64) -> Self {
389 self.0.set_usage_AT(n);
390 self
391 }
392 pub fn usages_other(mut self, n: u64) -> Self {
393 self.0.set_usage_other(n);
394 self
395 }
396 pub fn build(self) -> Restriction {
397 self.0
398 }
399}
400
401#[derive(Serialize, Deserialize, Debug, Clone)]
402pub struct Profile {
403 #[serde(skip_serializing_if = "Option::is_none")]
404 capabilities: Option<HashSet<Capability>>,
405
406 #[serde(skip_serializing_if = "Option::is_none")]
407 restrictions: Option<HashSet<Restriction>>,
408
409 #[serde(skip_serializing_if = "Option::is_none")]
410 rotation: Option<Rotation>,
411}
412
413impl Default for Profile {
414 fn default() -> Self {
415 Self {
416 capabilities: None,
417 restrictions: None,
418 rotation: None,
419 }
420 }
421}
422
423impl Profile {
424 pub fn new() -> Self {
425 Profile::default()
426 }
427
428 pub fn add_capabilities<'a, I>(&mut self, capabilities: I)
429 where
430 I: IntoIterator<Item = &'a Capability>,
431 {
432 if let Some(ref mut caps) = self.capabilities {
433 caps.extend(capabilities.into_iter().cloned())
434 } else {
435 self.capabilities = Some(capabilities.into_iter().cloned().collect())
436 }
437 }
438
439 pub fn add_restrictions<'a, I>(&mut self, restrictions: I)
440 where
441 I: IntoIterator<Item = &'a Restriction>,
442 {
443 if let Some(ref mut rests) = self.restrictions {
444 rests.extend(restrictions.into_iter().cloned())
445 } else {
446 self.restrictions = Some(restrictions.into_iter().cloned().collect())
447 }
448 }
449 pub fn set_rotation(&mut self, rotation: &Rotation) {
450 self.rotation = Some(*rotation);
451 }
452 pub fn builder() -> ProfileBuilder {
453 ProfileBuilder(Profile::default())
454 }
455}
456
457pub struct ProfileBuilder(Profile);
458
459impl ProfileBuilder {
460 pub fn add_capabilities<'a, I>(mut self, capabilities: I) -> Self
461 where
462 I: IntoIterator<Item = &'a Capability>,
463 {
464 self.0.add_capabilities(capabilities);
465 self
466 }
467
468 pub fn add_restrictions<'a, I>(mut self, restrictions: I) -> Self
469 where
470 I: IntoIterator<Item = &'a Restriction>,
471 {
472 self.0.add_restrictions(restrictions);
473 self
474 }
475 pub fn set_rotation(mut self, rotation: &Rotation) -> Self {
476 self.0.set_rotation(rotation);
477 self
478 }
479 pub fn build(self) -> Profile {
480 self.0
481 }
482}