1use super::ScimEntryGetQuery;
3use super::ScimOauth2ClaimMapJoinChar;
4use crate::attribute::{Attribute, SubAttribute};
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 time::format_description::well_known::Rfc3339;
14use time::OffsetDateTime;
15use uuid::Uuid;
16
17pub type ScimSshPublicKeys = Vec<ScimSshPublicKey>;
18
19#[derive(Deserialize, Serialize, Debug, Clone)]
20#[serde(deny_unknown_fields, rename_all = "camelCase")]
21pub struct ScimSshPublicKey {
22 pub label: String,
23 pub value: SshPublicKey,
24}
25
26#[serde_as]
27#[skip_serializing_none]
28#[derive(Deserialize, Serialize, Debug, Clone)]
29#[serde(deny_unknown_fields, rename_all = "camelCase")]
30pub struct ScimReference {
31 pub uuid: Option<Uuid>,
32 pub value: Option<String>,
33}
34
35impl<T> From<T> for ScimReference
36where
37 T: AsRef<str>,
38{
39 fn from(value: T) -> Self {
40 ScimReference {
41 uuid: None,
42 value: Some(value.as_ref().to_string()),
43 }
44 }
45}
46
47pub type ScimReferences = Vec<ScimReference>;
48
49#[serde_as]
50#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
51#[serde(transparent)]
52pub struct ScimDateTime {
53 #[serde_as(as = "Rfc3339")]
54 pub date_time: OffsetDateTime,
55}
56
57#[serde_as]
58#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
59#[serde(rename_all = "camelCase")]
60pub struct ScimCertificate {
61 #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
62 pub der: Vec<u8>,
63}
64
65#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
66#[serde(rename_all = "camelCase")]
67pub struct ScimAddress {
68 pub street_address: String,
69 pub locality: String,
70 pub region: String,
71 pub postal_code: String,
72 pub country: String,
73}
74
75#[serde_as]
76#[derive(Deserialize, Serialize, Debug, Clone)]
77#[serde(rename_all = "camelCase")]
78pub struct ScimOAuth2ClaimMap {
79 pub group: Option<String>,
80 pub group_uuid: Option<Uuid>,
81 pub claim: String,
82 pub join_char: ScimOauth2ClaimMapJoinChar,
83 pub values: BTreeSet<String>,
84}
85
86#[serde_as]
87#[derive(Deserialize, Serialize, Debug, Clone)]
88#[serde(rename_all = "camelCase")]
89pub struct ScimOAuth2ScopeMap {
90 pub group: Option<String>,
91 pub group_uuid: Option<Uuid>,
92 pub scopes: BTreeSet<String>,
93}
94
95#[serde_as]
96#[derive(Serialize, Debug, Clone)]
97#[serde(rename_all = "snake_case")]
98pub struct ScimEntryApplicationPost {
99 pub name: String,
100 pub displayname: String,
101 pub linked_group: ScimReference,
102}
103
104#[serde_as]
105#[derive(Deserialize, Debug, Clone)]
106#[serde(rename_all = "snake_case")]
107pub struct ScimEntryApplication {
108 #[serde(flatten)]
109 pub header: ScimEntryHeader,
110
111 pub name: String,
112 pub displayname: String,
113
114 pub linked_group: Vec<super::ScimReference>,
115
116 #[serde(flatten)]
117 pub attrs: BTreeMap<Attribute, JsonValue>,
118}
119
120#[derive(Serialize, Debug, Clone)]
121pub struct ScimEntryPutKanidm {
122 pub id: Uuid,
123 #[serde(flatten)]
124 pub attrs: BTreeMap<Attribute, Option<super::server::ScimValueKanidm>>,
125}
126
127#[serde_as]
128#[derive(Deserialize, Serialize, Debug, Clone)]
129pub struct ScimStrings(#[serde_as(as = "OneOrMany<_, PreferMany>")] pub Vec<String>);
130
131#[derive(Debug, Clone, Deserialize, Default)]
132pub struct ScimEntryPostGeneric {
133 #[serde(flatten)]
135 pub attrs: BTreeMap<Attribute, JsonValue>,
136}
137
138#[derive(Debug, Clone, Deserialize, Default)]
139pub struct ScimEntryPutGeneric {
140 pub id: Uuid,
142
143 #[serde(flatten)]
144 pub query: ScimEntryGetQuery,
148
149 #[serde(flatten)]
155 pub attrs: BTreeMap<Attribute, Option<JsonValue>>,
156}
157
158impl TryFrom<ScimEntryPutKanidm> for ScimEntryPutGeneric {
159 type Error = serde_json::Error;
160
161 fn try_from(value: ScimEntryPutKanidm) -> Result<Self, Self::Error> {
162 let ScimEntryPutKanidm { id, attrs } = value;
163
164 let attrs = attrs
165 .into_iter()
166 .map(|(attr, value)| {
167 if let Some(v) = value {
168 serde_json::to_value(v).map(|json_value| (attr, Some(json_value)))
169 } else {
170 Ok((attr, None))
171 }
172 })
173 .collect::<Result<_, _>>()?;
174
175 Ok(ScimEntryPutGeneric {
176 id,
177 attrs,
178 query: Default::default(),
179 })
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
184pub struct AttrPath {
185 pub a: Attribute,
186 pub s: Option<SubAttribute>,
187}
188
189impl From<Attribute> for AttrPath {
190 fn from(a: Attribute) -> Self {
191 Self { a, s: None }
192 }
193}
194
195impl From<(Attribute, SubAttribute)> for AttrPath {
196 fn from((a, s): (Attribute, SubAttribute)) -> Self {
197 Self { a, s: Some(s) }
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
202pub enum ScimFilter {
203 Or(Box<ScimFilter>, Box<ScimFilter>),
204 And(Box<ScimFilter>, Box<ScimFilter>),
205 Not(Box<ScimFilter>),
206
207 Present(AttrPath),
208 Equal(AttrPath, JsonValue),
209 NotEqual(AttrPath, JsonValue),
210 Contains(AttrPath, JsonValue),
211 StartsWith(AttrPath, JsonValue),
212 EndsWith(AttrPath, JsonValue),
213 Greater(AttrPath, JsonValue),
214 Less(AttrPath, JsonValue),
215 GreaterOrEqual(AttrPath, JsonValue),
216 LessOrEqual(AttrPath, JsonValue),
217
218 Complex(Attribute, Box<ScimComplexFilter>),
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
222pub enum ScimComplexFilter {
223 Or(Box<ScimComplexFilter>, Box<ScimComplexFilter>),
224 And(Box<ScimComplexFilter>, Box<ScimComplexFilter>),
225 Not(Box<ScimComplexFilter>),
226
227 Present(SubAttribute),
228 Equal(SubAttribute, JsonValue),
229 NotEqual(SubAttribute, JsonValue),
230 Contains(SubAttribute, JsonValue),
231 StartsWith(SubAttribute, JsonValue),
232 EndsWith(SubAttribute, JsonValue),
233 Greater(SubAttribute, JsonValue),
234 Less(SubAttribute, JsonValue),
235 GreaterOrEqual(SubAttribute, JsonValue),
236 LessOrEqual(SubAttribute, JsonValue),
237}