1use super::{ScimEntryGeneric, ScimEntryGetQuery, ScimMail, ScimOauth2ClaimMapJoinChar};
3use crate::attribute::Attribute;
4use crate::v1::OutboundMessage;
5use scim_proto::ScimEntryHeader;
6use serde::{Deserialize, Serialize};
7use serde_json::Value as JsonValue;
8use serde_with::formats::PreferMany;
9use serde_with::OneOrMany;
10use serde_with::{base64, formats, serde_as, skip_serializing_none};
11use sshkey_attest::proto::PublicKey as SshPublicKey;
12use std::collections::{BTreeMap, BTreeSet};
13use std::num::NonZeroU64;
14use time::format_description::well_known::Rfc3339;
15use time::OffsetDateTime;
16use utoipa::ToSchema;
17use uuid::Uuid;
18
19pub type ScimSshPublicKeys = Vec<ScimSshPublicKey>;
20
21#[derive(Deserialize, Serialize, Debug, Clone)]
22#[serde(deny_unknown_fields, rename_all = "camelCase")]
23pub struct ScimSshPublicKey {
24 pub label: String,
25 pub value: SshPublicKey,
26}
27
28#[serde_as]
29#[skip_serializing_none]
30#[derive(Deserialize, Serialize, Debug, Clone)]
31#[serde(deny_unknown_fields, rename_all = "camelCase")]
32pub struct ScimReference {
33 pub uuid: Option<Uuid>,
34 pub value: Option<String>,
35}
36
37impl<T> From<T> for ScimReference
38where
39 T: AsRef<str>,
40{
41 fn from(value: T) -> Self {
42 ScimReference {
43 uuid: None,
44 value: Some(value.as_ref().to_string()),
45 }
46 }
47}
48
49pub type ScimReferences = Vec<ScimReference>;
50
51#[serde_as]
52#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
53#[serde(transparent)]
54pub struct ScimDateTime {
55 #[serde_as(as = "Rfc3339")]
56 pub date_time: OffsetDateTime,
57}
58
59#[serde_as]
60#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
61#[serde(rename_all = "camelCase")]
62pub struct ScimCertificate {
63 #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
64 pub der: Vec<u8>,
65}
66
67#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
68#[serde(rename_all = "camelCase")]
69pub struct ScimAddress {
70 pub street_address: String,
71 pub locality: String,
72 pub region: String,
73 pub postal_code: String,
74 pub country: String,
75}
76
77#[serde_as]
78#[derive(Deserialize, Serialize, Debug, Clone)]
79#[serde(rename_all = "camelCase")]
80pub struct ScimOAuth2ClaimMap {
81 pub group: Option<String>,
82 pub group_uuid: Option<Uuid>,
83 pub claim: String,
84 pub join_char: ScimOauth2ClaimMapJoinChar,
85 pub values: BTreeSet<String>,
86}
87
88#[serde_as]
89#[derive(Deserialize, Serialize, Debug, Clone)]
90#[serde(rename_all = "camelCase")]
91pub struct ScimOAuth2ScopeMap {
92 pub group: Option<String>,
93 pub group_uuid: Option<Uuid>,
94 pub scopes: BTreeSet<String>,
95}
96
97#[serde_as]
98#[derive(Serialize, Deserialize, Clone, Debug)]
99#[serde(rename_all = "camelCase")]
100pub struct ScimListEntry {
101 pub schemas: Vec<String>,
102 pub total_results: u64,
103 pub items_per_page: Option<NonZeroU64>,
104 pub start_index: Option<NonZeroU64>,
105 pub resources: Vec<ScimEntryGeneric>,
106}
107
108#[serde_as]
109#[derive(Serialize, Debug, Clone)]
110#[serde(rename_all = "snake_case")]
111pub struct ScimEntryApplicationPost {
112 pub name: String,
113 pub displayname: String,
114 pub linked_group: ScimReference,
115}
116
117#[serde_as]
118#[derive(Deserialize, Debug, Clone)]
119#[serde(rename_all = "snake_case")]
120pub struct ScimEntryApplication {
121 #[serde(flatten)]
122 pub header: ScimEntryHeader,
123
124 pub name: String,
125 pub displayname: String,
126
127 pub linked_group: Vec<super::ScimReference>,
128
129 #[serde(flatten)]
130 pub attrs: BTreeMap<Attribute, JsonValue>,
131}
132
133#[serde_as]
134#[derive(Deserialize, Clone, Debug)]
135#[serde(rename_all = "camelCase")]
136pub struct ScimListApplication {
137 pub schemas: Vec<String>,
138 pub total_results: u64,
139 pub items_per_page: Option<NonZeroU64>,
140 pub start_index: Option<NonZeroU64>,
141 pub resources: Vec<ScimEntryApplication>,
142}
143
144#[serde_as]
145#[derive(Serialize, Deserialize, Debug, Clone)]
146#[serde(rename_all = "snake_case")]
147pub struct ScimEntryMessage {
148 #[serde(flatten)]
149 pub header: ScimEntryHeader,
150
151 pub message_template: OutboundMessage,
152 pub send_after: ScimDateTime,
153 pub delete_after: ScimDateTime,
154 pub sent_at: Option<ScimDateTime>,
155 pub mail_destination: Vec<ScimMail>,
156
157 #[serde(flatten)]
158 pub attrs: BTreeMap<Attribute, JsonValue>,
159}
160
161#[serde_as]
162#[derive(Serialize, Deserialize, Clone, Debug)]
163#[serde(rename_all = "camelCase")]
164pub struct ScimListMessage {
165 pub schemas: Vec<String>,
166 pub total_results: u64,
167 pub items_per_page: Option<NonZeroU64>,
168 pub start_index: Option<NonZeroU64>,
169 pub resources: Vec<ScimEntryMessage>,
170}
171
172#[serde_as]
173#[derive(Serialize, Deserialize, Debug, Clone)]
174#[serde(rename_all = "snake_case")]
175pub struct ScimEntrySchemaClass {
176 #[serde(flatten)]
177 pub header: ScimEntryHeader,
178
179 #[serde(flatten)]
183 pub attrs: BTreeMap<Attribute, JsonValue>,
184}
185
186#[serde_as]
187#[derive(Deserialize, Clone, Debug)]
188#[serde(rename_all = "camelCase")]
189pub struct ScimListSchemaClass {
190 pub schemas: Vec<String>,
191 pub total_results: u64,
192 pub items_per_page: Option<NonZeroU64>,
193 pub start_index: Option<NonZeroU64>,
194 pub resources: Vec<ScimEntrySchemaClass>,
195}
196
197#[serde_as]
198#[derive(Deserialize, Debug, Clone, Serialize)]
199#[serde(rename_all = "snake_case")]
200pub struct ScimEntrySchemaAttribute {
201 #[serde(flatten)]
202 pub header: ScimEntryHeader,
203
204 pub attributename: String,
205 pub description: String,
206 pub multivalue: bool,
208 pub unique: bool,
209 pub syntax: String,
210 #[serde(flatten)]
212 pub attrs: BTreeMap<Attribute, JsonValue>,
213}
214
215#[serde_as]
216#[derive(Deserialize, Clone, Debug)]
217#[serde(rename_all = "camelCase")]
218pub struct ScimListSchemaAttribute {
219 pub schemas: Vec<String>,
220 pub total_results: u64,
221 pub items_per_page: Option<NonZeroU64>,
222 pub start_index: Option<NonZeroU64>,
223 pub resources: Vec<ScimEntrySchemaAttribute>,
224}
225
226#[derive(Serialize, Debug, Clone)]
227pub struct ScimEntryPutKanidm {
228 pub id: Uuid,
229 #[serde(flatten)]
230 pub attrs: BTreeMap<Attribute, Option<super::server::ScimValueKanidm>>,
231}
232
233#[serde_as]
234#[derive(Deserialize, Serialize, Debug, Clone)]
235pub struct ScimStrings(#[serde_as(as = "OneOrMany<_, PreferMany>")] pub Vec<String>);
236
237#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
238pub struct ScimEntryPostGeneric {
239 #[serde(flatten)]
241 #[schema(value_type = Object, additional_properties = true)]
242 pub attrs: BTreeMap<Attribute, JsonValue>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
246pub struct ScimEntryPutGeneric {
247 pub id: Uuid,
249
250 #[serde(flatten)]
251 pub query: ScimEntryGetQuery,
255
256 #[schema(value_type = BTreeMap<String, Value>)]
262 #[serde(flatten)]
263 pub attrs: BTreeMap<Attribute, Option<JsonValue>>,
264}
265
266impl TryFrom<ScimEntryPutKanidm> for ScimEntryPutGeneric {
267 type Error = serde_json::Error;
268
269 fn try_from(value: ScimEntryPutKanidm) -> Result<Self, Self::Error> {
270 let ScimEntryPutKanidm { id, attrs } = value;
271
272 let attrs = attrs
273 .into_iter()
274 .map(|(attr, value)| {
275 if let Some(v) = value {
276 serde_json::to_value(v).map(|json_value| (attr, Some(json_value)))
277 } else {
278 Ok((attr, None))
279 }
280 })
281 .collect::<Result<_, _>>()?;
282
283 Ok(ScimEntryPutGeneric {
284 id,
285 attrs,
286 query: Default::default(),
287 })
288 }
289}