1use super::UiHint;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeSet;
4use std::fmt;
5use time::OffsetDateTime;
6use utoipa::ToSchema;
7use uuid::Uuid;
8
9use serde_with::skip_serializing_none;
10
11#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
12#[serde(rename_all = "lowercase")]
13pub enum UatPurpose {
14 ReadOnly,
15 ReadWrite {
16 #[serde(with = "time::serde::timestamp::option")]
19 expiry: Option<time::OffsetDateTime>,
20 },
21}
22
23#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
32#[skip_serializing_none]
33#[serde(rename_all = "lowercase")]
34pub struct UserAuthToken {
35 pub session_id: Uuid,
36 #[serde(with = "time::serde::timestamp")]
37 pub issued_at: time::OffsetDateTime,
38 #[serde(with = "time::serde::timestamp::option")]
41 pub expiry: Option<time::OffsetDateTime>,
42 pub purpose: UatPurpose,
43 pub uuid: Uuid,
44 pub displayname: String,
45 pub spn: String,
46 pub mail_primary: Option<String>,
47 pub ui_hints: BTreeSet<UiHint>,
48
49 pub limit_search_max_results: Option<u64>,
50 pub limit_search_max_filter_test: Option<u64>,
51}
52
53impl fmt::Display for UserAuthToken {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 writeln!(f, "spn: {}", self.spn)?;
56 writeln!(f, "uuid: {}", self.uuid)?;
57 writeln!(f, "display: {}", self.displayname)?;
58 if let Some(exp) = self.expiry {
59 writeln!(f, "expiry: {exp}")?;
60 } else {
61 writeln!(f, "expiry: -")?;
62 }
63 match &self.purpose {
64 UatPurpose::ReadOnly => writeln!(f, "purpose: read only")?,
65 UatPurpose::ReadWrite {
66 expiry: Some(expiry),
67 } => writeln!(f, "purpose: read write (expiry: {expiry})")?,
68 UatPurpose::ReadWrite { expiry: None } => {
69 writeln!(f, "purpose: read write (expiry: none)")?
70 }
71 }
72 Ok(())
73 }
74}
75
76impl PartialEq for UserAuthToken {
77 fn eq(&self, other: &Self) -> bool {
78 self.session_id == other.session_id
79 }
80}
81
82impl Eq for UserAuthToken {}
83
84pub enum PrivilegesActive {
85 True,
87 ReauthRequired,
89 False,
91}
92
93impl UserAuthToken {
94 pub fn name(&self) -> &str {
95 self.spn.split_once('@').map(|x| x.0).unwrap_or(&self.spn)
96 }
97
98 pub fn purpose_privilege_state(&self, ct: time::OffsetDateTime) -> PrivilegesActive {
101 match self.purpose {
102 UatPurpose::ReadWrite { expiry: Some(exp) } if ct < exp => PrivilegesActive::True,
103 UatPurpose::ReadWrite { expiry: Some(_) } | UatPurpose::ReadWrite { expiry: None } => {
105 PrivilegesActive::ReauthRequired
106 }
107 UatPurpose::ReadOnly => PrivilegesActive::False,
108 }
109 }
110}
111
112#[derive(Debug, Serialize, Deserialize, Clone, Default, ToSchema)]
113#[serde(rename_all = "lowercase")]
114pub enum ApiTokenPurpose {
115 #[default]
116 ReadOnly,
117 ReadWrite,
118 Synchronise,
119}
120
121#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
122#[serde(rename_all = "lowercase")]
123pub struct ApiToken {
124 pub account_id: Uuid,
126 pub token_id: Uuid,
127 pub label: String,
128 #[serde(with = "time::serde::timestamp::option")]
129 pub expiry: Option<time::OffsetDateTime>,
130 #[serde(with = "time::serde::timestamp")]
131 pub issued_at: time::OffsetDateTime,
132 #[serde(default)]
134 pub purpose: ApiTokenPurpose,
135}
136
137impl fmt::Display for ApiToken {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 writeln!(f, "account_id: {}", self.account_id)?;
140 writeln!(f, "token_id: {}", self.token_id)?;
141 writeln!(f, "label: {}", self.label)?;
142 writeln!(f, "issued at: {}", self.issued_at)?;
143 if let Some(expiry) = self.expiry {
144 #[allow(clippy::expect_used)]
146 let expiry_str = expiry
147 .to_offset(
148 time::UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH)
149 .unwrap_or(time::UtcOffset::UTC),
150 )
151 .format(&time::format_description::well_known::Rfc3339)
152 .expect("Failed to format timestamp to RFC3339");
153 writeln!(f, "token expiry: {expiry_str}")
154 } else {
155 writeln!(f, "token expiry: never")
156 }
157 }
158}
159
160impl PartialEq for ApiToken {
161 fn eq(&self, other: &Self) -> bool {
162 self.token_id == other.token_id
163 }
164}
165
166impl Eq for ApiToken {}
167
168#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
171pub struct RadiusAuthToken {
172 pub name: String,
173 pub displayname: String,
174 pub uuid: String,
175 pub secret: String,
176 pub groups: Vec<Group>,
177}
178
179impl fmt::Display for RadiusAuthToken {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 writeln!(f, "name: {}", self.name)?;
182 writeln!(f, "displayname: {}", self.displayname)?;
183 writeln!(f, "uuid: {}", self.uuid)?;
184 writeln!(f, "secret: {}", self.secret)?;
185 self.groups
186 .iter()
187 .try_for_each(|g| writeln!(f, "group: {g}"))
188 }
189}
190
191#[derive(Debug, Serialize, Deserialize, Clone)]
192#[serde(rename_all = "lowercase")]
193pub struct ScimSyncToken {
194 pub token_id: Uuid,
196 #[serde(with = "time::serde::timestamp")]
197 pub issued_at: time::OffsetDateTime,
198 #[serde(default)]
199 pub purpose: ApiTokenPurpose,
200}
201
202#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
203pub struct Group {
204 pub spn: String,
205 pub uuid: String,
206}
207
208impl fmt::Display for Group {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 write!(f, "[ spn: {}, ", self.spn)?;
211 write!(f, "uuid: {} ]", self.uuid)
212 }
213}