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 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, Debug, Clone)]
99#[serde(rename_all = "snake_case")]
100pub struct ScimEntryApplicationPost {
101 pub name: String,
102 pub displayname: String,
103 pub linked_group: ScimReference,
104}
105
106#[serde_as]
107#[derive(Deserialize, Debug, Clone)]
108#[serde(rename_all = "snake_case")]
109pub struct ScimEntryApplication {
110 #[serde(flatten)]
111 pub header: ScimEntryHeader,
112
113 pub name: String,
114 pub displayname: String,
115
116 pub linked_group: Vec<super::ScimReference>,
117
118 #[serde(flatten)]
119 pub attrs: BTreeMap<Attribute, JsonValue>,
120}
121
122#[serde_as]
123#[derive(Deserialize, Clone, Debug)]
124#[serde(rename_all = "camelCase")]
125pub struct ScimListApplication {
126 pub schemas: Vec<String>,
127 pub total_results: u64,
128 pub items_per_page: Option<NonZeroU64>,
129 pub start_index: Option<NonZeroU64>,
130 pub resources: Vec<ScimEntryApplication>,
131}
132
133#[derive(Serialize, Debug, Clone)]
134pub struct ScimEntryPutKanidm {
135 pub id: Uuid,
136 #[serde(flatten)]
137 pub attrs: BTreeMap<Attribute, Option<super::server::ScimValueKanidm>>,
138}
139
140#[serde_as]
141#[derive(Deserialize, Serialize, Debug, Clone)]
142pub struct ScimStrings(#[serde_as(as = "OneOrMany<_, PreferMany>")] pub Vec<String>);
143
144#[derive(Debug, Clone, Deserialize, Default, ToSchema)]
145pub struct ScimEntryPostGeneric {
146 #[serde(flatten)]
148 #[schema(value_type = Object, additional_properties = true)]
149 pub attrs: BTreeMap<Attribute, JsonValue>,
150}
151
152#[derive(Debug, Clone, Deserialize, Default)]
153pub struct ScimEntryPutGeneric {
154 pub id: Uuid,
156
157 #[serde(flatten)]
158 pub query: ScimEntryGetQuery,
162
163 #[serde(flatten)]
169 pub attrs: BTreeMap<Attribute, Option<JsonValue>>,
170}
171
172impl TryFrom<ScimEntryPutKanidm> for ScimEntryPutGeneric {
173 type Error = serde_json::Error;
174
175 fn try_from(value: ScimEntryPutKanidm) -> Result<Self, Self::Error> {
176 let ScimEntryPutKanidm { id, attrs } = value;
177
178 let attrs = attrs
179 .into_iter()
180 .map(|(attr, value)| {
181 if let Some(v) = value {
182 serde_json::to_value(v).map(|json_value| (attr, Some(json_value)))
183 } else {
184 Ok((attr, None))
185 }
186 })
187 .collect::<Result<_, _>>()?;
188
189 Ok(ScimEntryPutGeneric {
190 id,
191 attrs,
192 query: Default::default(),
193 })
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
198pub struct AttrPath {
199 pub a: Attribute,
200 pub s: Option<SubAttribute>,
201}
202
203impl From<Attribute> for AttrPath {
204 fn from(a: Attribute) -> Self {
205 Self { a, s: None }
206 }
207}
208
209impl From<(Attribute, SubAttribute)> for AttrPath {
210 fn from((a, s): (Attribute, SubAttribute)) -> Self {
211 Self { a, s: Some(s) }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
216pub enum ScimFilter {
217 Or(Box<ScimFilter>, Box<ScimFilter>),
218 And(Box<ScimFilter>, Box<ScimFilter>),
219 Not(Box<ScimFilter>),
220
221 Present(AttrPath),
222 Equal(AttrPath, JsonValue),
223 NotEqual(AttrPath, JsonValue),
224 Contains(AttrPath, JsonValue),
225 StartsWith(AttrPath, JsonValue),
226 EndsWith(AttrPath, JsonValue),
227 Greater(AttrPath, JsonValue),
228 Less(AttrPath, JsonValue),
229 GreaterOrEqual(AttrPath, JsonValue),
230 LessOrEqual(AttrPath, JsonValue),
231
232 Complex(Attribute, Box<ScimComplexFilter>),
233}
234
235#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
236pub enum ScimComplexFilter {
237 Or(Box<ScimComplexFilter>, Box<ScimComplexFilter>),
238 And(Box<ScimComplexFilter>, Box<ScimComplexFilter>),
239 Not(Box<ScimComplexFilter>),
240
241 Present(SubAttribute),
242 Equal(SubAttribute, JsonValue),
243 NotEqual(SubAttribute, JsonValue),
244 Contains(SubAttribute, JsonValue),
245 StartsWith(SubAttribute, JsonValue),
246 EndsWith(SubAttribute, JsonValue),
247 Greater(SubAttribute, JsonValue),
248 Less(SubAttribute, JsonValue),
249 GreaterOrEqual(SubAttribute, JsonValue),
250 LessOrEqual(SubAttribute, JsonValue),
251}