1use super::ScimMail;
2use super::ScimOauth2ClaimMapJoinChar;
3use super::ScimSshPublicKey;
4use crate::attribute::Attribute;
5use crate::internal::UiHint;
6use scim_proto::ScimEntryHeader;
7use serde::Serialize;
8use serde_with::{base64, formats, hex::Hex, serde_as, skip_serializing_none};
9use std::collections::{BTreeMap, BTreeSet};
10use time::format_description::well_known::Rfc3339;
11use time::OffsetDateTime;
12use url::Url;
13use utoipa::ToSchema;
14use uuid::Uuid;
15
16#[serde_as]
20#[skip_serializing_none]
21#[derive(Serialize, Debug, Clone, ToSchema)]
22pub struct ScimEntryKanidm {
23 #[serde(flatten)]
24 pub header: ScimEntryHeader,
25
26 pub ext_access_check: Option<ScimEffectiveAccess>,
27 #[serde(flatten)]
28 pub attrs: BTreeMap<Attribute, ScimValueKanidm>,
29}
30
31#[derive(Serialize, Debug, Clone, ToSchema)]
32pub enum ScimAttributeEffectiveAccess {
33 Grant,
35 Deny,
37 Allow(BTreeSet<Attribute>),
39}
40
41impl ScimAttributeEffectiveAccess {
42 pub fn check(&self, attr: &Attribute) -> bool {
44 match self {
45 Self::Grant => true,
46 Self::Deny => false,
47 Self::Allow(set) => set.contains(attr),
48 }
49 }
50}
51
52#[derive(Serialize, Debug, Clone, ToSchema)]
53#[serde(rename_all = "camelCase")]
54pub struct ScimEffectiveAccess {
55 pub ident: Uuid,
57 pub delete: bool,
59 pub search: ScimAttributeEffectiveAccess,
61 pub modify_present: ScimAttributeEffectiveAccess,
63 pub modify_remove: ScimAttributeEffectiveAccess,
65}
66
67#[derive(Serialize, Debug, Clone, ToSchema)]
68#[serde(rename_all = "camelCase")]
69pub struct ScimAddress {
70 pub formatted: String,
71 pub street_address: String,
72 pub locality: String,
73 pub region: String,
74 pub postal_code: String,
75 pub country: String,
76}
77
78#[derive(Serialize, Debug, Clone, ToSchema)]
79#[serde(rename_all = "camelCase")]
80pub struct ScimApplicationPassword {
81 pub uuid: Uuid,
82 pub application_uuid: Uuid,
83 pub label: String,
84}
85
86#[serde_as]
87#[derive(Serialize, Debug, Clone, ToSchema)]
88#[serde(rename_all = "camelCase")]
89pub struct ScimBinary {
90 pub label: String,
91 #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
92 pub value: Vec<u8>,
93}
94
95#[serde_as]
96#[derive(Serialize, Debug, Clone, ToSchema)]
97#[serde(rename_all = "camelCase")]
98pub struct ScimCertificate {
99 #[serde_as(as = "Hex")]
100 pub s256: Vec<u8>,
101 #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
102 pub der: Vec<u8>,
103}
104
105#[serde_as]
106#[derive(Serialize, Debug, Clone, ToSchema)]
107#[serde(rename_all = "camelCase")]
108pub struct ScimAuditString {
109 #[serde_as(as = "Rfc3339")]
110 pub date_time: OffsetDateTime,
111 pub value: String,
112}
113
114#[derive(Serialize, Debug, Clone, ToSchema)]
115#[serde(rename_all = "camelCase")]
116pub enum ScimIntentTokenState {
117 Valid,
118 InProgress,
119 Consumed,
120}
121
122#[serde_as]
123#[derive(Serialize, Debug, Clone, ToSchema)]
124#[serde(rename_all = "camelCase")]
125pub struct ScimIntentToken {
126 pub token_id: String,
127 pub state: ScimIntentTokenState,
128 #[serde_as(as = "Rfc3339")]
129 pub expires: OffsetDateTime,
130}
131
132#[serde_as]
133#[derive(Serialize, Debug, Clone, ToSchema)]
134#[serde(rename_all = "camelCase")]
135pub struct ScimKeyInternal {
136 pub key_id: String,
137 pub status: String,
138 pub usage: String,
139 #[serde_as(as = "Rfc3339")]
140 pub valid_from: OffsetDateTime,
141}
142
143#[serde_as]
144#[skip_serializing_none]
145#[derive(Serialize, Debug, Clone, ToSchema)]
146#[serde(rename_all = "camelCase")]
147pub struct ScimAuthSession {
148 pub id: Uuid,
149 #[serde_as(as = "Option<Rfc3339>")]
150 pub expires: Option<OffsetDateTime>,
151 #[serde_as(as = "Option<Rfc3339>")]
152 pub revoked: Option<OffsetDateTime>,
153 #[serde_as(as = "Rfc3339")]
154 pub issued_at: OffsetDateTime,
155 pub issued_by: Uuid,
156 pub credential_id: Uuid,
157 pub auth_type: String,
158 pub session_scope: String,
159}
160
161#[serde_as]
162#[skip_serializing_none]
163#[derive(Serialize, Debug, Clone, ToSchema)]
164#[serde(rename_all = "camelCase")]
165pub struct ScimOAuth2Session {
166 pub id: Uuid,
167 pub parent_id: Option<Uuid>,
168 pub client_id: Uuid,
169 #[serde_as(as = "Rfc3339")]
170 pub issued_at: OffsetDateTime,
171 #[serde_as(as = "Option<Rfc3339>")]
172 pub expires: Option<OffsetDateTime>,
173 #[serde_as(as = "Option<Rfc3339>")]
174 pub revoked: Option<OffsetDateTime>,
175}
176
177#[serde_as]
178#[skip_serializing_none]
179#[derive(Serialize, Debug, Clone, ToSchema)]
180#[serde(rename_all = "camelCase")]
181pub struct ScimApiToken {
182 pub id: Uuid,
183 pub label: String,
184 #[serde_as(as = "Option<Rfc3339>")]
185 pub expires: Option<OffsetDateTime>,
186 #[serde_as(as = "Rfc3339")]
187 pub issued_at: OffsetDateTime,
188 pub issued_by: Uuid,
189 pub scope: String,
190}
191
192#[serde_as]
193#[derive(Serialize, Debug, Clone, ToSchema)]
194#[serde(rename_all = "camelCase")]
195pub struct ScimOAuth2ScopeMap {
196 pub group: String,
197 pub group_uuid: Uuid,
198 pub scopes: BTreeSet<String>,
199}
200
201#[serde_as]
202#[derive(Serialize, Debug, Clone, ToSchema)]
203#[serde(rename_all = "camelCase")]
204pub struct ScimOAuth2ClaimMap {
205 pub group: String,
206 pub group_uuid: Uuid,
207 pub claim: String,
208 pub join_char: ScimOauth2ClaimMapJoinChar,
209 pub values: BTreeSet<String>,
210}
211
212#[derive(Serialize, Debug, Clone, PartialEq, Eq, ToSchema)]
213#[serde(rename_all = "camelCase")]
214pub struct ScimReference {
215 pub uuid: Uuid,
216 pub value: String,
217}
218
219#[serde_as]
224#[derive(Serialize, Debug, Clone, ToSchema)]
225#[serde(untagged)]
226pub enum ScimValueKanidm {
227 Bool(bool),
228 Uint32(u32),
229 Integer(i64),
230 Decimal(f64),
231 String(String),
232 DateTime(#[serde_as(as = "Rfc3339")] OffsetDateTime),
233 Reference(Url),
234 Uuid(Uuid),
235 EntryReference(ScimReference),
236 EntryReferences(Vec<ScimReference>),
237
238 ArrayString(Vec<String>),
240 ArrayDateTime(#[serde_as(as = "Vec<Rfc3339>")] Vec<OffsetDateTime>),
241 ArrayUuid(Vec<Uuid>),
242 ArrayBinary(Vec<ScimBinary>),
243 ArrayCertificate(Vec<ScimCertificate>),
244
245 Address(Vec<ScimAddress>),
246 Mail(Vec<ScimMail>),
247 ApplicationPassword(Vec<ScimApplicationPassword>),
248 AuditString(Vec<ScimAuditString>),
249 SshPublicKey(Vec<ScimSshPublicKey>),
250 AuthSession(Vec<ScimAuthSession>),
251 OAuth2Session(Vec<ScimOAuth2Session>),
252 ApiToken(Vec<ScimApiToken>),
253 IntentToken(Vec<ScimIntentToken>),
254 OAuth2ScopeMap(Vec<ScimOAuth2ScopeMap>),
255 OAuth2ClaimMap(Vec<ScimOAuth2ClaimMap>),
256 KeyInternal(Vec<ScimKeyInternal>),
257 UiHints(Vec<UiHint>),
258}
259
260#[serde_as]
261#[derive(Serialize, Debug, Clone, ToSchema)]
262pub struct ScimPerson {
263 pub uuid: Uuid,
264 pub name: String,
265 pub displayname: String,
266 pub spn: String,
267 pub description: Option<String>,
268 pub mails: Vec<ScimMail>,
269 pub managed_by: Option<ScimReference>,
270 pub groups: Vec<ScimReference>,
271}
272
273impl TryFrom<ScimEntryKanidm> for ScimPerson {
274 type Error = ();
275
276 fn try_from(scim_entry: ScimEntryKanidm) -> Result<Self, Self::Error> {
277 let uuid = scim_entry.header.id;
278 let name = scim_entry
279 .attrs
280 .get(&Attribute::Name)
281 .and_then(|v| match v {
282 ScimValueKanidm::String(s) => Some(s.clone()),
283 _ => None,
284 })
285 .ok_or(())?;
286
287 let displayname = scim_entry
288 .attrs
289 .get(&Attribute::DisplayName)
290 .and_then(|v| match v {
291 ScimValueKanidm::String(s) => Some(s.clone()),
292 _ => None,
293 })
294 .ok_or(())?;
295
296 let spn = scim_entry
297 .attrs
298 .get(&Attribute::Spn)
299 .and_then(|v| match v {
300 ScimValueKanidm::String(s) => Some(s.clone()),
301 _ => None,
302 })
303 .ok_or(())?;
304
305 let description = scim_entry
306 .attrs
307 .get(&Attribute::Description)
308 .and_then(|v| match v {
309 ScimValueKanidm::String(s) => Some(s.clone()),
310 _ => None,
311 });
312
313 let mails = scim_entry
314 .attrs
315 .get(&Attribute::Mail)
316 .and_then(|v| match v {
317 ScimValueKanidm::Mail(m) => Some(m.clone()),
318 _ => None,
319 })
320 .unwrap_or_default();
321
322 let groups = scim_entry
323 .attrs
324 .get(&Attribute::DirectMemberOf)
325 .and_then(|v| match v {
326 ScimValueKanidm::EntryReferences(v) => Some(v.clone()),
327 _ => None,
328 })
329 .unwrap_or_default();
330
331 let managed_by = scim_entry
332 .attrs
333 .get(&Attribute::EntryManagedBy)
334 .and_then(|v| match v {
335 ScimValueKanidm::EntryReference(v) => Some(v.clone()),
336 _ => None,
337 });
338
339 Ok(ScimPerson {
340 uuid,
341 name,
342 displayname,
343 spn,
344 description,
345 mails,
346 managed_by,
347 groups,
348 })
349 }
350}
351
352impl From<bool> for ScimValueKanidm {
353 fn from(b: bool) -> Self {
354 Self::Bool(b)
355 }
356}
357
358impl From<OffsetDateTime> for ScimValueKanidm {
359 fn from(odt: OffsetDateTime) -> Self {
360 Self::DateTime(odt)
361 }
362}
363
364impl From<Vec<UiHint>> for ScimValueKanidm {
365 fn from(set: Vec<UiHint>) -> Self {
366 Self::UiHints(set)
367 }
368}
369
370impl From<Vec<OffsetDateTime>> for ScimValueKanidm {
371 fn from(set: Vec<OffsetDateTime>) -> Self {
372 Self::ArrayDateTime(set)
373 }
374}
375
376impl From<String> for ScimValueKanidm {
377 fn from(s: String) -> Self {
378 Self::String(s)
379 }
380}
381
382impl From<&str> for ScimValueKanidm {
383 fn from(s: &str) -> Self {
384 Self::String(s.to_string())
385 }
386}
387
388impl From<Vec<String>> for ScimValueKanidm {
389 fn from(set: Vec<String>) -> Self {
390 Self::ArrayString(set)
391 }
392}
393
394impl From<Uuid> for ScimValueKanidm {
395 fn from(u: Uuid) -> Self {
396 Self::Uuid(u)
397 }
398}
399
400impl From<Vec<Uuid>> for ScimValueKanidm {
401 fn from(set: Vec<Uuid>) -> Self {
402 Self::ArrayUuid(set)
403 }
404}
405
406impl From<u32> for ScimValueKanidm {
407 fn from(u: u32) -> Self {
408 Self::Uint32(u)
409 }
410}
411
412impl From<Vec<ScimAddress>> for ScimValueKanidm {
413 fn from(set: Vec<ScimAddress>) -> Self {
414 Self::Address(set)
415 }
416}
417
418impl From<Vec<ScimMail>> for ScimValueKanidm {
419 fn from(set: Vec<ScimMail>) -> Self {
420 Self::Mail(set)
421 }
422}
423
424impl From<Vec<ScimApplicationPassword>> for ScimValueKanidm {
425 fn from(set: Vec<ScimApplicationPassword>) -> Self {
426 Self::ApplicationPassword(set)
427 }
428}
429
430impl From<Vec<ScimAuditString>> for ScimValueKanidm {
431 fn from(set: Vec<ScimAuditString>) -> Self {
432 Self::AuditString(set)
433 }
434}
435
436impl From<Vec<ScimBinary>> for ScimValueKanidm {
437 fn from(set: Vec<ScimBinary>) -> Self {
438 Self::ArrayBinary(set)
439 }
440}
441
442impl From<Vec<ScimCertificate>> for ScimValueKanidm {
443 fn from(set: Vec<ScimCertificate>) -> Self {
444 Self::ArrayCertificate(set)
445 }
446}
447
448impl From<Vec<ScimSshPublicKey>> for ScimValueKanidm {
449 fn from(set: Vec<ScimSshPublicKey>) -> Self {
450 Self::SshPublicKey(set)
451 }
452}
453
454impl From<Vec<ScimAuthSession>> for ScimValueKanidm {
455 fn from(set: Vec<ScimAuthSession>) -> Self {
456 Self::AuthSession(set)
457 }
458}
459
460impl From<Vec<ScimOAuth2Session>> for ScimValueKanidm {
461 fn from(set: Vec<ScimOAuth2Session>) -> Self {
462 Self::OAuth2Session(set)
463 }
464}
465
466impl From<Vec<ScimApiToken>> for ScimValueKanidm {
467 fn from(set: Vec<ScimApiToken>) -> Self {
468 Self::ApiToken(set)
469 }
470}
471
472impl From<Vec<ScimIntentToken>> for ScimValueKanidm {
473 fn from(set: Vec<ScimIntentToken>) -> Self {
474 Self::IntentToken(set)
475 }
476}
477
478impl From<Vec<ScimOAuth2ScopeMap>> for ScimValueKanidm {
479 fn from(set: Vec<ScimOAuth2ScopeMap>) -> Self {
480 Self::OAuth2ScopeMap(set)
481 }
482}
483
484impl From<Vec<ScimOAuth2ClaimMap>> for ScimValueKanidm {
485 fn from(set: Vec<ScimOAuth2ClaimMap>) -> Self {
486 Self::OAuth2ClaimMap(set)
487 }
488}
489
490impl From<Vec<ScimKeyInternal>> for ScimValueKanidm {
491 fn from(set: Vec<ScimKeyInternal>) -> Self {
492 Self::KeyInternal(set)
493 }
494}