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