kanidm_proto/scim_v1/
client.rs

1//! These are types that a client will send to the server.
2use super::ScimEntryGetQuery;
3use super::ScimOauth2ClaimMapJoinChar;
4use crate::attribute::{Attribute, SubAttribute};
5use serde::{Deserialize, Serialize};
6use serde_json::Value as JsonValue;
7use serde_with::formats::PreferMany;
8use serde_with::OneOrMany;
9use serde_with::{base64, formats, serde_as, skip_serializing_none};
10use sshkey_attest::proto::PublicKey as SshPublicKey;
11use std::collections::{BTreeMap, BTreeSet};
12use time::format_description::well_known::Rfc3339;
13use time::OffsetDateTime;
14use uuid::Uuid;
15
16pub type ScimSshPublicKeys = Vec<ScimSshPublicKey>;
17
18#[derive(Deserialize, Serialize, Debug, Clone)]
19#[serde(deny_unknown_fields, rename_all = "camelCase")]
20pub struct ScimSshPublicKey {
21    pub label: String,
22    pub value: SshPublicKey,
23}
24
25#[serde_as]
26#[skip_serializing_none]
27#[derive(Deserialize, Serialize, Debug, Clone)]
28#[serde(deny_unknown_fields, rename_all = "camelCase")]
29pub struct ScimReference {
30    pub uuid: Option<Uuid>,
31    pub value: Option<String>,
32}
33
34pub type ScimReferences = Vec<ScimReference>;
35
36#[serde_as]
37#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
38#[serde(transparent)]
39pub struct ScimDateTime {
40    #[serde_as(as = "Rfc3339")]
41    pub date_time: OffsetDateTime,
42}
43
44#[serde_as]
45#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
46#[serde(rename_all = "camelCase")]
47pub struct ScimCertificate {
48    #[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
49    pub der: Vec<u8>,
50}
51
52#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
53#[serde(rename_all = "camelCase")]
54pub struct ScimAddress {
55    pub street_address: String,
56    pub locality: String,
57    pub region: String,
58    pub postal_code: String,
59    pub country: String,
60}
61
62#[serde_as]
63#[derive(Deserialize, Serialize, Debug, Clone)]
64#[serde(rename_all = "camelCase")]
65pub struct ScimOAuth2ClaimMap {
66    pub group: Option<String>,
67    pub group_uuid: Option<Uuid>,
68    pub claim: String,
69    pub join_char: ScimOauth2ClaimMapJoinChar,
70    pub values: BTreeSet<String>,
71}
72
73#[serde_as]
74#[derive(Deserialize, Serialize, Debug, Clone)]
75#[serde(rename_all = "camelCase")]
76pub struct ScimOAuth2ScopeMap {
77    pub group: Option<String>,
78    pub group_uuid: Option<Uuid>,
79    pub scopes: BTreeSet<String>,
80}
81
82#[derive(Serialize, Debug, Clone)]
83pub struct ScimEntryPutKanidm {
84    pub id: Uuid,
85    #[serde(flatten)]
86    pub attrs: BTreeMap<Attribute, Option<super::server::ScimValueKanidm>>,
87}
88
89#[serde_as]
90#[derive(Deserialize, Serialize, Debug, Clone)]
91pub struct ScimStrings(#[serde_as(as = "OneOrMany<_, PreferMany>")] pub Vec<String>);
92
93#[derive(Debug, Clone, Deserialize, Default)]
94pub struct ScimEntryPutGeneric {
95    // id is only used to target the entry in question
96    pub id: Uuid,
97
98    #[serde(flatten)]
99    /// Non-standard extension - allow query options to be set in a put request. This
100    /// is because a put request also returns the entry state post put, so we want
101    /// to allow putters to adjust and control what is returned here.
102    pub query: ScimEntryGetQuery,
103
104    // external_id can't be set by put
105    // meta is skipped on put
106    // Schemas are decoded as part of "attrs".
107    /// Update an attribute to contain the following value state.
108    /// If the attribute is None, it is removed.
109    #[serde(flatten)]
110    pub attrs: BTreeMap<Attribute, Option<JsonValue>>,
111}
112
113impl TryFrom<ScimEntryPutKanidm> for ScimEntryPutGeneric {
114    type Error = serde_json::Error;
115
116    fn try_from(value: ScimEntryPutKanidm) -> Result<Self, Self::Error> {
117        let ScimEntryPutKanidm { id, attrs } = value;
118
119        let attrs = attrs
120            .into_iter()
121            .map(|(attr, value)| {
122                if let Some(v) = value {
123                    serde_json::to_value(v).map(|json_value| (attr, Some(json_value)))
124                } else {
125                    Ok((attr, None))
126                }
127            })
128            .collect::<Result<_, _>>()?;
129
130        Ok(ScimEntryPutGeneric {
131            id,
132            attrs,
133            query: Default::default(),
134        })
135    }
136}
137
138#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
139pub struct AttrPath {
140    pub a: Attribute,
141    pub s: Option<SubAttribute>,
142}
143
144impl From<Attribute> for AttrPath {
145    fn from(a: Attribute) -> Self {
146        Self { a, s: None }
147    }
148}
149
150impl From<(Attribute, SubAttribute)> for AttrPath {
151    fn from((a, s): (Attribute, SubAttribute)) -> Self {
152        Self { a, s: Some(s) }
153    }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
157pub enum ScimFilter {
158    Or(Box<ScimFilter>, Box<ScimFilter>),
159    And(Box<ScimFilter>, Box<ScimFilter>),
160    Not(Box<ScimFilter>),
161
162    Present(AttrPath),
163    Equal(AttrPath, JsonValue),
164    NotEqual(AttrPath, JsonValue),
165    Contains(AttrPath, JsonValue),
166    StartsWith(AttrPath, JsonValue),
167    EndsWith(AttrPath, JsonValue),
168    Greater(AttrPath, JsonValue),
169    Less(AttrPath, JsonValue),
170    GreaterOrEqual(AttrPath, JsonValue),
171    LessOrEqual(AttrPath, JsonValue),
172
173    Complex(Attribute, Box<ScimComplexFilter>),
174}
175
176#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
177pub enum ScimComplexFilter {
178    Or(Box<ScimComplexFilter>, Box<ScimComplexFilter>),
179    And(Box<ScimComplexFilter>, Box<ScimComplexFilter>),
180    Not(Box<ScimComplexFilter>),
181
182    Present(SubAttribute),
183    Equal(SubAttribute, JsonValue),
184    NotEqual(SubAttribute, JsonValue),
185    Contains(SubAttribute, JsonValue),
186    StartsWith(SubAttribute, JsonValue),
187    EndsWith(SubAttribute, JsonValue),
188    Greater(SubAttribute, JsonValue),
189    Less(SubAttribute, JsonValue),
190    GreaterOrEqual(SubAttribute, JsonValue),
191    LessOrEqual(SubAttribute, JsonValue),
192}