kanidmd_core/actors/
v1_scim.rs

1use super::{QueryServerReadV1, QueryServerWriteV1};
2use kanidm_proto::scim_v1::{
3    client::ScimEntryPostGeneric, client::ScimFilter, server::ScimEntryKanidm, ScimEntryGetQuery,
4    ScimSyncRequest, ScimSyncState,
5};
6use kanidmd_lib::idm::scim::{
7    GenerateScimSyncTokenEvent, ScimSyncFinaliseEvent, ScimSyncTerminateEvent, ScimSyncUpdateEvent,
8};
9
10use kanidmd_lib::server::scim::{ScimCreateEvent, ScimDeleteEvent};
11
12use kanidmd_lib::idm::server::IdmServerTransaction;
13use kanidmd_lib::prelude::*;
14
15impl QueryServerWriteV1 {
16    #[instrument(
17        level = "info",
18        skip_all,
19        fields(uuid = ?eventid)
20    )]
21    pub async fn handle_sync_account_token_generate(
22        &self,
23        client_auth_info: ClientAuthInfo,
24        uuid_or_name: String,
25        label: String,
26        eventid: Uuid,
27    ) -> Result<String, OperationError> {
28        let ct = duration_from_epoch_now();
29        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
30        let ident = idms_prox_write
31            .validate_client_auth_info_to_ident(client_auth_info, ct)
32            .map_err(|e| {
33                admin_error!(err = ?e, "Invalid identity");
34                e
35            })?;
36
37        let target = idms_prox_write
38            .qs_write
39            .name_to_uuid(uuid_or_name.as_str())
40            .map_err(|e| {
41                admin_error!(err = ?e, "Error resolving id to target");
42                e
43            })?;
44
45        let gte = GenerateScimSyncTokenEvent {
46            ident,
47            target,
48            label,
49        };
50
51        idms_prox_write
52            .scim_sync_generate_token(&gte, ct)
53            .map(|token| token.to_string())
54            .and_then(|r| idms_prox_write.commit().map(|_| r))
55    }
56
57    #[instrument(
58        level = "info",
59        skip_all,
60        fields(uuid = ?eventid)
61    )]
62    pub async fn handle_sync_account_token_destroy(
63        &self,
64        client_auth_info: ClientAuthInfo,
65        uuid_or_name: String,
66        eventid: Uuid,
67    ) -> Result<(), OperationError> {
68        let ct = duration_from_epoch_now();
69        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
70        let ident = idms_prox_write
71            .validate_client_auth_info_to_ident(client_auth_info, ct)
72            .map_err(|e| {
73                admin_error!(err = ?e, "Invalid identity");
74                e
75            })?;
76
77        let target = idms_prox_write
78            .qs_write
79            .name_to_uuid(uuid_or_name.as_str())
80            .map_err(|e| {
81                admin_error!(err = ?e, "Error resolving id to target");
82                e
83            })?;
84
85        idms_prox_write
86            .sync_account_destroy_token(&ident, target, ct)
87            .and_then(|r| idms_prox_write.commit().map(|_| r))
88    }
89
90    #[instrument(
91        level = "info",
92        skip_all,
93        fields(uuid = ?eventid)
94    )]
95    pub async fn handle_sync_account_finalise(
96        &self,
97        client_auth_info: ClientAuthInfo,
98        uuid_or_name: String,
99        eventid: Uuid,
100    ) -> Result<(), OperationError> {
101        let ct = duration_from_epoch_now();
102        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
103        let ident = idms_prox_write
104            .validate_client_auth_info_to_ident(client_auth_info, ct)
105            .map_err(|e| {
106                admin_error!(err = ?e, "Invalid identity");
107                e
108            })?;
109
110        let target = idms_prox_write
111            .qs_write
112            .name_to_uuid(uuid_or_name.as_str())
113            .map_err(|e| {
114                admin_error!(err = ?e, "Error resolving id to target");
115                e
116            })?;
117
118        let sfe = ScimSyncFinaliseEvent { ident, target };
119
120        idms_prox_write
121            .scim_sync_finalise(&sfe)
122            .and_then(|r| idms_prox_write.commit().map(|_| r))
123    }
124
125    #[instrument(
126        level = "info",
127        skip_all,
128        fields(uuid = ?eventid)
129    )]
130    pub async fn handle_sync_account_terminate(
131        &self,
132        client_auth_info: ClientAuthInfo,
133        uuid_or_name: String,
134        eventid: Uuid,
135    ) -> Result<(), OperationError> {
136        let ct = duration_from_epoch_now();
137        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
138        let ident = idms_prox_write
139            .validate_client_auth_info_to_ident(client_auth_info, ct)
140            .map_err(|e| {
141                admin_error!(err = ?e, "Invalid identity");
142                e
143            })?;
144
145        let target = idms_prox_write
146            .qs_write
147            .name_to_uuid(uuid_or_name.as_str())
148            .map_err(|e| {
149                admin_error!(err = ?e, "Error resolving id to target");
150                e
151            })?;
152
153        let ste = ScimSyncTerminateEvent { ident, target };
154
155        idms_prox_write
156            .scim_sync_terminate(&ste)
157            .and_then(|r| idms_prox_write.commit().map(|_| r))
158    }
159
160    #[instrument(
161        level = "info",
162        skip_all,
163        fields(uuid = ?eventid)
164    )]
165    pub async fn handle_scim_sync_apply(
166        &self,
167        client_auth_info: ClientAuthInfo,
168        changes: ScimSyncRequest,
169        eventid: Uuid,
170    ) -> Result<(), OperationError> {
171        let ct = duration_from_epoch_now();
172        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
173
174        let ident =
175            idms_prox_write.validate_sync_client_auth_info_to_ident(client_auth_info, ct)?;
176
177        let sse = ScimSyncUpdateEvent { ident };
178
179        idms_prox_write
180            .scim_sync_apply(&sse, &changes, ct)
181            .and_then(|r| idms_prox_write.commit().map(|_| r))
182    }
183
184    #[instrument(
185        level = "info",
186        skip_all,
187        fields(uuid = ?eventid)
188    )]
189    pub async fn scim_entry_create(
190        &self,
191        client_auth_info: ClientAuthInfo,
192        eventid: Uuid,
193        classes: &[EntryClass],
194        entry: ScimEntryPostGeneric,
195    ) -> Result<ScimEntryKanidm, OperationError> {
196        let ct = duration_from_epoch_now();
197        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
198        let ident = idms_prox_write
199            .validate_client_auth_info_to_ident(client_auth_info, ct)
200            .map_err(|e| {
201                admin_error!(err = ?e, "Invalid identity");
202                e
203            })?;
204
205        let scim_create_event =
206            ScimCreateEvent::try_from(ident, classes, entry, &mut idms_prox_write.qs_write)?;
207
208        idms_prox_write
209            .qs_write
210            .scim_create(scim_create_event)
211            .and_then(|r| idms_prox_write.commit().map(|_| r))
212    }
213
214    #[instrument(
215        level = "info",
216        skip_all,
217        fields(uuid = ?eventid)
218    )]
219    pub async fn scim_entry_id_delete(
220        &self,
221        client_auth_info: ClientAuthInfo,
222        eventid: Uuid,
223        uuid_or_name: String,
224        class: EntryClass,
225    ) -> Result<(), OperationError> {
226        let ct = duration_from_epoch_now();
227        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
228        let ident = idms_prox_write
229            .validate_client_auth_info_to_ident(client_auth_info, ct)
230            .map_err(|e| {
231                admin_error!(err = ?e, "Invalid identity");
232                e
233            })?;
234
235        let target = idms_prox_write
236            .qs_write
237            .name_to_uuid(uuid_or_name.as_str())
238            .map_err(|e| {
239                admin_error!(err = ?e, "Error resolving id to target");
240                e
241            })?;
242
243        let scim_delete_event = ScimDeleteEvent::new(ident, target, class);
244
245        idms_prox_write
246            .qs_write
247            .scim_delete(scim_delete_event)
248            .and_then(|r| idms_prox_write.commit().map(|_| r))
249    }
250}
251
252impl QueryServerReadV1 {
253    #[instrument(
254        level = "info",
255        skip_all,
256        fields(uuid = ?eventid)
257    )]
258    pub async fn handle_scim_sync_status(
259        &self,
260        client_auth_info: ClientAuthInfo,
261        eventid: Uuid,
262    ) -> Result<ScimSyncState, OperationError> {
263        let ct = duration_from_epoch_now();
264        let mut idms_prox_read = self.idms.proxy_read().await?;
265
266        let ident = idms_prox_read.validate_sync_client_auth_info_to_ident(client_auth_info, ct)?;
267
268        idms_prox_read.scim_sync_get_state(&ident)
269    }
270
271    #[instrument(
272        level = "info",
273        skip_all,
274        fields(uuid = ?eventid)
275    )]
276    pub async fn scim_entry_id_get(
277        &self,
278        client_auth_info: ClientAuthInfo,
279        eventid: Uuid,
280        uuid_or_name: String,
281        class: EntryClass,
282        query: ScimEntryGetQuery,
283    ) -> Result<ScimEntryKanidm, OperationError> {
284        let ct = duration_from_epoch_now();
285        let mut idms_prox_read = self.idms.proxy_read().await?;
286        let ident = idms_prox_read
287            .validate_client_auth_info_to_ident(client_auth_info, ct)
288            .inspect_err(|err| {
289                error!(?err, "Invalid identity");
290            })?;
291
292        let target_uuid = idms_prox_read
293            .qs_read
294            .name_to_uuid(uuid_or_name.as_str())
295            .inspect_err(|err| {
296                error!(?err, "Error resolving id to target");
297            })?;
298
299        idms_prox_read
300            .qs_read
301            .scim_entry_id_get_ext(target_uuid, class, query, ident)
302    }
303
304    #[instrument(
305        level = "info",
306        skip_all,
307        fields(uuid = ?eventid)
308    )]
309    pub async fn scim_entry_search(
310        &self,
311        client_auth_info: ClientAuthInfo,
312        eventid: Uuid,
313        filter: ScimFilter,
314        query: ScimEntryGetQuery,
315    ) -> Result<Vec<ScimEntryKanidm>, OperationError> {
316        let ct = duration_from_epoch_now();
317        let mut idms_prox_read = self.idms.proxy_read().await?;
318        let ident = idms_prox_read
319            .validate_client_auth_info_to_ident(client_auth_info, ct)
320            .inspect_err(|err| {
321                error!(?err, "Invalid identity");
322            })?;
323
324        idms_prox_read.qs_read.scim_search_ext(ident, filter, query)
325    }
326}