oidc_agent_rs/
mytoken.rs

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    ///Mytoken `tokeninfo:introspect` value.
13    Introspect,
14    ///Mytoken `tokeninfo:subtokens` value.
15    Subtokens,
16    ///Mytoken `tokeninfo:history` value.
17    History,
18    ///Mytoken `tokeninfo` value.
19    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    /// Mytoken `manage_mytoken:list` value.
36    List,
37    ///Mytoken `manage_mytoken:revoke` value.
38    Revoke,
39    ///Mytoken `manage_mytoken:history` value.
40    History,
41    ///Mytoken `manage_mytoken` value.
42    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    ///Mytoken `settings:grants:ssh` value.
59    Ssh,
60    ///Mytoken `settings:grants` value.
61    Grants,
62    ///Mytoken `settings` value
63    All,
64    ///Mytoken `read@settings:grants:ssh` value.
65    ReadSsh,
66    ///Mytoken `read@settings:grants` value.
67    ReadGrants,
68    ///Mytoken `read@settings` value.
69    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>, //Seconds I guess
284
285    #[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}