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