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