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
33#[serde_as]
34#[skip_serializing_none]
35#[derive(Serialize, Clone, Debug, Default)]
36#[serde(deny_unknown_fields, rename_all = "camelCase")]
37pub struct ScimListResponse {
38 pub schemas: Vec<String>,
39 pub total_results: u64,
40 pub items_per_page: Option<NonZeroU64>,
41 pub start_index: Option<NonZeroU64>,
42 pub resources: Vec<ScimEntryKanidm>,
43}
44
45#[derive(Serialize, Debug, Clone, ToSchema)]
46pub enum ScimAttributeEffectiveAccess {
47 Grant,
49 Deny,
51 Allow(BTreeSet<Attribute>),
53}
54
55impl ScimAttributeEffectiveAccess {
56 pub fn check(&self, attr: &Attribute) -> bool {
58 match self {
59 Self::Grant => true,
60 Self::Deny => false,
61 Self::Allow(set) => set.contains(attr),
62 }
63 }
64
65 pub fn check_any(&self, attrs: &BTreeSet<Attribute>) -> bool {
67 match self {
68 Self::Grant => true,
69 Self::Deny => false,
70 Self::Allow(set) => attrs.intersection(set).next().is_some(),
71 }
72 }
73}
74
75#[derive(Serialize, Debug, Clone, ToSchema)]
76#[serde(rename_all = "camelCase")]
77pub struct ScimEffectiveAccess {
78 pub ident: Uuid,
80 pub delete: bool,
82 pub search: ScimAttributeEffectiveAccess,
84 pub modify_present: ScimAttributeEffectiveAccess,
86 pub modify_remove: ScimAttributeEffectiveAccess,
88}
89
90#[derive(Serialize, Debug, Clone, ToSchema)]
91#[serde(rename_all = "camelCase")]
92pub struct ScimAddress {
93 pub formatted: String,
94 pub street_address: String,
95 pub locality: String,
96 pub region: String,
97 pub postal_code: String,
98 pub country: String,
99}
100
101#[derive(Serialize, Debug, Clone, ToSchema)]
102#[serde(rename_all = "camelCase")]
103pub struct ScimApplicationPassword {
104 pub uuid: Uuid,
105 pub application_uuid: Uuid,
106 pub label: String,
107}
108
109#[serde_as]
110#[derive(Serialize, Debug, Clone, ToSchema)]
111#[serde(rename_all = "camelCase")]
112pub struct ScimBinary {
113 pub label: String,
114 #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
115 pub value: Vec<u8>,
116}
117
118#[serde_as]
119#[derive(Serialize, Debug, Clone, ToSchema)]
120#[serde(rename_all = "camelCase")]
121pub struct ScimCertificate {
122 #[serde_as(as = "Hex")]
123 pub s256: Vec<u8>,
124 #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
125 pub der: Vec<u8>,
126}
127
128#[serde_as]
129#[derive(Serialize, Debug, Clone, ToSchema)]
130#[serde(rename_all = "camelCase")]
131pub struct ScimAuditString {
132 #[serde_as(as = "Rfc3339")]
133 pub date_time: OffsetDateTime,
134 pub value: String,
135}
136
137#[derive(Serialize, Debug, Clone, ToSchema)]
138#[serde(rename_all = "camelCase")]
139pub enum ScimIntentTokenState {
140 Valid,
141 InProgress,
142 Consumed,
143}
144
145#[serde_as]
146#[derive(Serialize, Debug, Clone, ToSchema)]
147#[serde(rename_all = "camelCase")]
148pub struct ScimIntentToken {
149 pub token_id: String,
150 pub state: ScimIntentTokenState,
151 #[serde_as(as = "Rfc3339")]
152 pub expires: OffsetDateTime,
153}
154
155#[serde_as]
156#[derive(Serialize, Debug, Clone, ToSchema)]
157#[serde(rename_all = "camelCase")]
158pub struct ScimKeyInternal {
159 pub key_id: String,
160 pub status: String,
161 pub usage: String,
162 #[serde_as(as = "Rfc3339")]
163 pub valid_from: OffsetDateTime,
164}
165
166#[serde_as]
167#[skip_serializing_none]
168#[derive(Serialize, Debug, Clone, ToSchema)]
169#[serde(rename_all = "camelCase")]
170pub struct ScimAuthSession {
171 pub id: Uuid,
172 #[serde_as(as = "Option<Rfc3339>")]
173 pub expires: Option<OffsetDateTime>,
174 #[serde_as(as = "Option<Rfc3339>")]
175 pub revoked: Option<OffsetDateTime>,
176 #[serde_as(as = "Rfc3339")]
177 pub issued_at: OffsetDateTime,
178 pub issued_by: Uuid,
179 pub credential_id: Uuid,
180 pub auth_type: String,
181 pub session_scope: String,
182}
183
184#[serde_as]
185#[skip_serializing_none]
186#[derive(Serialize, Debug, Clone, ToSchema)]
187#[serde(rename_all = "camelCase")]
188pub struct ScimOAuth2Session {
189 pub id: Uuid,
190 pub parent_id: Option<Uuid>,
191 pub client_id: Uuid,
192 #[serde_as(as = "Rfc3339")]
193 pub issued_at: OffsetDateTime,
194 #[serde_as(as = "Option<Rfc3339>")]
195 pub expires: Option<OffsetDateTime>,
196 #[serde_as(as = "Option<Rfc3339>")]
197 pub revoked: Option<OffsetDateTime>,
198}
199
200#[serde_as]
201#[skip_serializing_none]
202#[derive(Serialize, Debug, Clone, ToSchema)]
203#[serde(rename_all = "camelCase")]
204pub struct ScimApiToken {
205 pub id: Uuid,
206 pub label: String,
207 #[serde_as(as = "Option<Rfc3339>")]
208 pub expires: Option<OffsetDateTime>,
209 #[serde_as(as = "Rfc3339")]
210 pub issued_at: OffsetDateTime,
211 pub issued_by: Uuid,
212 pub scope: String,
213}
214
215#[serde_as]
216#[derive(Serialize, Debug, Clone, ToSchema)]
217#[serde(rename_all = "camelCase")]
218pub struct ScimOAuth2ScopeMap {
219 pub group: String,
220 pub group_uuid: Uuid,
221 pub scopes: BTreeSet<String>,
222}
223
224#[serde_as]
225#[derive(Serialize, Debug, Clone, ToSchema)]
226#[serde(rename_all = "camelCase")]
227pub struct ScimOAuth2ClaimMap {
228 pub group: String,
229 pub group_uuid: Uuid,
230 pub claim: String,
231 pub join_char: ScimOauth2ClaimMapJoinChar,
232 pub values: BTreeSet<String>,
233}
234
235#[derive(Serialize, Debug, Clone, PartialEq, Eq, ToSchema)]
236#[serde(rename_all = "camelCase")]
237pub struct ScimReference {
238 pub uuid: Uuid,
239 pub value: String,
240}
241
242#[serde_as]
247#[derive(Serialize, Debug, Clone, ToSchema)]
248#[serde(untagged)]
249pub enum ScimValueKanidm {
250 Bool(bool),
251 Uint32(u32),
252 Integer(i64),
253 Decimal(f64),
254 String(String),
255
256 DateTime(#[serde_as(as = "Rfc3339")] OffsetDateTime),
258 Reference(Url),
259 Uuid(Uuid),
260 EntryReference(ScimReference),
261 EntryReferences(Vec<ScimReference>),
262
263 ArrayString(Vec<String>),
264 ArrayDateTime(#[serde_as(as = "Vec<Rfc3339>")] Vec<OffsetDateTime>),
265 ArrayUuid(Vec<Uuid>),
266 ArrayBinary(Vec<ScimBinary>),
267 ArrayCertificate(Vec<ScimCertificate>),
268
269 Address(Vec<ScimAddress>),
270 Mail(Vec<ScimMail>),
271 ApplicationPassword(Vec<ScimApplicationPassword>),
272 AuditString(Vec<ScimAuditString>),
273 SshPublicKey(Vec<ScimSshPublicKey>),
274 AuthSession(Vec<ScimAuthSession>),
275 OAuth2Session(Vec<ScimOAuth2Session>),
276 ApiToken(Vec<ScimApiToken>),
277 IntentToken(Vec<ScimIntentToken>),
278 OAuth2ScopeMap(Vec<ScimOAuth2ScopeMap>),
279 OAuth2ClaimMap(Vec<ScimOAuth2ClaimMap>),
280 KeyInternal(Vec<ScimKeyInternal>),
281 UiHints(Vec<UiHint>),
282}
283
284#[serde_as]
285#[derive(Serialize, Debug, Clone, ToSchema)]
286pub struct ScimPerson {
287 pub uuid: Uuid,
288 pub name: String,
289 pub displayname: String,
290 pub spn: String,
291 pub description: Option<String>,
292 pub mails: Vec<ScimMail>,
293 pub managed_by: Option<ScimReference>,
294 pub groups: Vec<ScimReference>,
295}
296
297impl TryFrom<ScimEntryKanidm> for ScimPerson {
298 type Error = ();
299
300 fn try_from(scim_entry: ScimEntryKanidm) -> Result<Self, Self::Error> {
301 let uuid = scim_entry.header.id;
302 let name = scim_entry
303 .attrs
304 .get(&Attribute::Name)
305 .and_then(|v| match v {
306 ScimValueKanidm::String(s) => Some(s.clone()),
307 _ => None,
308 })
309 .ok_or(())?;
310
311 let displayname = scim_entry
312 .attrs
313 .get(&Attribute::DisplayName)
314 .and_then(|v| match v {
315 ScimValueKanidm::String(s) => Some(s.clone()),
316 _ => None,
317 })
318 .ok_or(())?;
319
320 let spn = scim_entry
321 .attrs
322 .get(&Attribute::Spn)
323 .and_then(|v| match v {
324 ScimValueKanidm::String(s) => Some(s.clone()),
325 _ => None,
326 })
327 .ok_or(())?;
328
329 let description = scim_entry
330 .attrs
331 .get(&Attribute::Description)
332 .and_then(|v| match v {
333 ScimValueKanidm::String(s) => Some(s.clone()),
334 _ => None,
335 });
336
337 let mails = scim_entry
338 .attrs
339 .get(&Attribute::Mail)
340 .and_then(|v| match v {
341 ScimValueKanidm::Mail(m) => Some(m.clone()),
342 _ => None,
343 })
344 .unwrap_or_default();
345
346 let groups = scim_entry
347 .attrs
348 .get(&Attribute::DirectMemberOf)
349 .and_then(|v| match v {
350 ScimValueKanidm::EntryReferences(v) => Some(v.clone()),
351 _ => None,
352 })
353 .unwrap_or_default();
354
355 let managed_by = scim_entry
356 .attrs
357 .get(&Attribute::EntryManagedBy)
358 .and_then(|v| match v {
359 ScimValueKanidm::EntryReference(v) => Some(v.clone()),
360 _ => None,
361 });
362
363 Ok(ScimPerson {
364 uuid,
365 name,
366 displayname,
367 spn,
368 description,
369 mails,
370 managed_by,
371 groups,
372 })
373 }
374}
375
376#[serde_as]
377#[derive(Serialize, Debug, Clone, ToSchema)]
378pub struct ScimGroup {
379 pub uuid: Uuid,
380 pub name: String,
381}
382
383impl TryFrom<ScimEntryKanidm> for ScimGroup {
384 type Error = ();
385
386 fn try_from(scim_entry: ScimEntryKanidm) -> Result<Self, Self::Error> {
387 let uuid = scim_entry.header.id;
388 let name = scim_entry
389 .attrs
390 .get(&Attribute::Name)
391 .and_then(|v| match v {
392 ScimValueKanidm::String(s) => Some(s.clone()),
393 _ => None,
394 })
395 .ok_or(())?;
396
397 Ok(ScimGroup { uuid, name })
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<ScimApplicationPassword>> for ScimValueKanidm {
474 fn from(set: Vec<ScimApplicationPassword>) -> 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}