kanidmd_core/actors/
v1_scim.rs

1use super::{QueryServerReadV1, QueryServerWriteV1};
2use kanidm_proto::scim_v1::{
3    client::{ScimEntryPostGeneric, ScimEntryPutGeneric},
4    server::{ScimEntryKanidm, ScimListResponse},
5    ScimApplicationPassword, ScimApplicationPasswordCreate, ScimEntryGetQuery, ScimFilter,
6    ScimSyncRequest, ScimSyncState,
7};
8use kanidmd_lib::idm::application::GenerateApplicationPasswordEvent;
9use kanidmd_lib::idm::scim::{
10    GenerateScimSyncTokenEvent, ScimSyncFinaliseEvent, ScimSyncTerminateEvent, ScimSyncUpdateEvent,
11};
12use kanidmd_lib::idm::server::IdmServerTransaction;
13use kanidmd_lib::prelude::*;
14use kanidmd_lib::server::scim::{ScimCreateEvent, ScimDeleteEvent, ScimEntryPutEvent};
15
16impl QueryServerWriteV1 {
17    #[instrument(
18        level = "info",
19        skip_all,
20        fields(uuid = ?eventid)
21    )]
22    pub async fn handle_sync_account_token_generate(
23        &self,
24        client_auth_info: ClientAuthInfo,
25        uuid_or_name: String,
26        label: String,
27        eventid: Uuid,
28    ) -> Result<String, OperationError> {
29        let ct = duration_from_epoch_now();
30        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
31        let ident = idms_prox_write
32            .validate_client_auth_info_to_ident(client_auth_info, ct)
33            .map_err(|e| {
34                admin_error!(err = ?e, "Invalid identity");
35                e
36            })?;
37
38        let target = idms_prox_write
39            .qs_write
40            .name_to_uuid(uuid_or_name.as_str())
41            .map_err(|e| {
42                admin_error!(err = ?e, "Error resolving id to target");
43                e
44            })?;
45
46        let gte = GenerateScimSyncTokenEvent {
47            ident,
48            target,
49            label,
50        };
51
52        idms_prox_write
53            .scim_sync_generate_token(&gte, ct)
54            .map(|token| token.to_string())
55            .and_then(|r| idms_prox_write.commit().map(|_| r))
56    }
57
58    #[instrument(
59        level = "info",
60        skip_all,
61        fields(uuid = ?eventid)
62    )]
63    pub async fn handle_sync_account_token_destroy(
64        &self,
65        client_auth_info: ClientAuthInfo,
66        uuid_or_name: String,
67        eventid: Uuid,
68    ) -> Result<(), OperationError> {
69        let ct = duration_from_epoch_now();
70        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
71        let ident = idms_prox_write
72            .validate_client_auth_info_to_ident(client_auth_info, ct)
73            .map_err(|e| {
74                admin_error!(err = ?e, "Invalid identity");
75                e
76            })?;
77
78        let target = idms_prox_write
79            .qs_write
80            .name_to_uuid(uuid_or_name.as_str())
81            .map_err(|e| {
82                admin_error!(err = ?e, "Error resolving id to target");
83                e
84            })?;
85
86        idms_prox_write
87            .sync_account_destroy_token(&ident, target, ct)
88            .and_then(|r| idms_prox_write.commit().map(|_| r))
89    }
90
91    #[instrument(
92        level = "info",
93        skip_all,
94        fields(uuid = ?eventid)
95    )]
96    pub async fn handle_sync_account_finalise(
97        &self,
98        client_auth_info: ClientAuthInfo,
99        uuid_or_name: String,
100        eventid: Uuid,
101    ) -> Result<(), OperationError> {
102        let ct = duration_from_epoch_now();
103        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
104        let ident = idms_prox_write
105            .validate_client_auth_info_to_ident(client_auth_info, ct)
106            .map_err(|e| {
107                admin_error!(err = ?e, "Invalid identity");
108                e
109            })?;
110
111        let target = idms_prox_write
112            .qs_write
113            .name_to_uuid(uuid_or_name.as_str())
114            .map_err(|e| {
115                admin_error!(err = ?e, "Error resolving id to target");
116                e
117            })?;
118
119        let sfe = ScimSyncFinaliseEvent { ident, target };
120
121        idms_prox_write
122            .scim_sync_finalise(&sfe)
123            .and_then(|r| idms_prox_write.commit().map(|_| r))
124    }
125
126    #[instrument(
127        level = "info",
128        skip_all,
129        fields(uuid = ?eventid)
130    )]
131    pub async fn handle_sync_account_terminate(
132        &self,
133        client_auth_info: ClientAuthInfo,
134        uuid_or_name: String,
135        eventid: Uuid,
136    ) -> Result<(), OperationError> {
137        let ct = duration_from_epoch_now();
138        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
139        let ident = idms_prox_write
140            .validate_client_auth_info_to_ident(client_auth_info, ct)
141            .map_err(|e| {
142                admin_error!(err = ?e, "Invalid identity");
143                e
144            })?;
145
146        let target = idms_prox_write
147            .qs_write
148            .name_to_uuid(uuid_or_name.as_str())
149            .map_err(|e| {
150                admin_error!(err = ?e, "Error resolving id to target");
151                e
152            })?;
153
154        let ste = ScimSyncTerminateEvent { ident, target };
155
156        idms_prox_write
157            .scim_sync_terminate(&ste)
158            .and_then(|r| idms_prox_write.commit().map(|_| r))
159    }
160
161    #[instrument(
162        level = "info",
163        skip_all,
164        fields(uuid = ?eventid)
165    )]
166    pub async fn handle_scim_sync_apply(
167        &self,
168        client_auth_info: ClientAuthInfo,
169        changes: ScimSyncRequest,
170        eventid: Uuid,
171    ) -> Result<(), OperationError> {
172        let ct = duration_from_epoch_now();
173        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
174
175        let ident =
176            idms_prox_write.validate_sync_client_auth_info_to_ident(client_auth_info, ct)?;
177
178        let sse = ScimSyncUpdateEvent { ident };
179
180        idms_prox_write
181            .scim_sync_apply(&sse, &changes, ct)
182            .and_then(|r| idms_prox_write.commit().map(|_| r))
183    }
184
185    #[instrument(
186        level = "info",
187        skip_all,
188        fields(uuid = ?eventid)
189    )]
190    pub async fn scim_entry_create(
191        &self,
192        client_auth_info: ClientAuthInfo,
193        eventid: Uuid,
194        classes: &[EntryClass],
195        entry: ScimEntryPostGeneric,
196    ) -> Result<ScimEntryKanidm, OperationError> {
197        let ct = duration_from_epoch_now();
198        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
199        let ident = idms_prox_write
200            .validate_client_auth_info_to_ident(client_auth_info, ct)
201            .map_err(|e| {
202                admin_error!(err = ?e, "Invalid identity");
203                e
204            })?;
205
206        let scim_create_event =
207            ScimCreateEvent::try_from(ident, classes, entry, &mut idms_prox_write.qs_write)?;
208
209        idms_prox_write
210            .qs_write
211            .scim_create(scim_create_event)
212            .and_then(|r| idms_prox_write.commit().map(|_| r))
213    }
214
215    #[instrument(
216        level = "info",
217        skip_all,
218        fields(uuid = ?eventid)
219    )]
220    pub async fn scim_entry_id_delete(
221        &self,
222        client_auth_info: ClientAuthInfo,
223        eventid: Uuid,
224        uuid_or_name: String,
225        class: EntryClass,
226    ) -> Result<(), OperationError> {
227        let ct = duration_from_epoch_now();
228        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
229        let ident = idms_prox_write
230            .validate_client_auth_info_to_ident(client_auth_info, ct)
231            .map_err(|e| {
232                admin_error!(err = ?e, "Invalid identity");
233                e
234            })?;
235
236        let target = idms_prox_write
237            .qs_write
238            .name_to_uuid(uuid_or_name.as_str())
239            .map_err(|e| {
240                admin_error!(err = ?e, "Error resolving id to target");
241                e
242            })?;
243
244        let scim_delete_event = ScimDeleteEvent::new(ident, target, class);
245
246        idms_prox_write
247            .qs_write
248            .scim_delete(scim_delete_event)
249            .and_then(|r| idms_prox_write.commit().map(|_| r))
250    }
251
252    #[instrument(
253        level = "info",
254        skip_all,
255        fields(uuid = ?eventid)
256    )]
257    pub async fn scim_person_application_create_password(
258        &self,
259        client_auth_info: ClientAuthInfo,
260        eventid: Uuid,
261        uuid_or_name: String,
262        request: ScimApplicationPasswordCreate,
263    ) -> Result<ScimApplicationPassword, OperationError> {
264        let ct = duration_from_epoch_now();
265        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
266        let ident = idms_prox_write
267            .validate_client_auth_info_to_ident(client_auth_info, ct)
268            .inspect_err(|err| error!(?err, "Invalid identity"))?;
269
270        let ScimApplicationPasswordCreate {
271            application_uuid,
272            label,
273        } = request;
274
275        let target = idms_prox_write
276            .qs_write
277            .name_to_uuid(uuid_or_name.as_str())
278            .inspect_err(|err| error!(?err, "Error resolving id to target"))?;
279
280        let generate_application_password_event =
281            GenerateApplicationPasswordEvent::from_parts(ident, target, application_uuid, label)?;
282
283        idms_prox_write
284            .generate_application_password(&generate_application_password_event)
285            .and_then(|(secret, uuid)| {
286                idms_prox_write.commit()?;
287
288                Ok(ScimApplicationPassword {
289                    uuid,
290                    label: generate_application_password_event.label,
291                    secret,
292                })
293            })
294    }
295
296    #[instrument(
297        level = "info",
298        skip_all,
299        fields(uuid = ?eventid)
300    )]
301    pub async fn scim_person_application_delete_password(
302        &self,
303        client_auth_info: ClientAuthInfo,
304        eventid: Uuid,
305        uuid_or_name: String,
306        apppwd_id: Uuid,
307    ) -> Result<(), OperationError> {
308        let ct = duration_from_epoch_now();
309        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
310        let ident = idms_prox_write
311            .validate_client_auth_info_to_ident(client_auth_info, ct)
312            .inspect_err(|err| error!(?err, "Invalid identity"))?;
313
314        let target = idms_prox_write
315            .qs_write
316            .name_to_uuid(uuid_or_name.as_str())
317            .inspect_err(|err| error!(?err, "Error resolving id to target"))?;
318
319        idms_prox_write
320            .application_password_delete(&ident, target, apppwd_id)
321            .and_then(|()| idms_prox_write.commit())
322    }
323
324    #[instrument(
325        level = "info",
326        skip_all,
327        fields(uuid = ?eventid)
328    )]
329    pub async fn handle_scim_entry_put(
330        &self,
331        client_auth_info: ClientAuthInfo,
332        eventid: Uuid,
333        generic: ScimEntryPutGeneric,
334    ) -> Result<ScimEntryKanidm, OperationError> {
335        let ct = duration_from_epoch_now();
336        let mut idms_prox_write = self.idms.proxy_write(ct).await?;
337        let ident = idms_prox_write
338            .validate_client_auth_info_to_ident(client_auth_info, ct)
339            .map_err(|op_err| {
340                admin_error!(err = ?op_err, "Invalid identity");
341                op_err
342            })?;
343
344        let scim_entry_put_event =
345            ScimEntryPutEvent::try_from(ident, generic, &mut idms_prox_write.qs_write)?;
346
347        idms_prox_write
348            .qs_write
349            .scim_put(scim_entry_put_event)
350            .and_then(|r| idms_prox_write.commit().map(|_| r))
351    }
352}
353
354impl QueryServerReadV1 {
355    #[instrument(
356        level = "info",
357        skip_all,
358        fields(uuid = ?eventid)
359    )]
360    pub async fn handle_scim_sync_status(
361        &self,
362        client_auth_info: ClientAuthInfo,
363        eventid: Uuid,
364    ) -> Result<ScimSyncState, OperationError> {
365        let ct = duration_from_epoch_now();
366        let mut idms_prox_read = self.idms.proxy_read().await?;
367
368        let ident = idms_prox_read.validate_sync_client_auth_info_to_ident(client_auth_info, ct)?;
369
370        idms_prox_read.scim_sync_get_state(&ident)
371    }
372
373    #[instrument(
374        level = "info",
375        skip_all,
376        fields(uuid = ?eventid)
377    )]
378    pub async fn scim_entry_id_get(
379        &self,
380        client_auth_info: ClientAuthInfo,
381        eventid: Uuid,
382        uuid_or_name: String,
383        class: EntryClass,
384        query: ScimEntryGetQuery,
385    ) -> Result<ScimEntryKanidm, OperationError> {
386        let ct = duration_from_epoch_now();
387        let mut idms_prox_read = self.idms.proxy_read().await?;
388        let ident = idms_prox_read
389            .validate_client_auth_info_to_ident(client_auth_info, ct)
390            .inspect_err(|err| {
391                error!(?err, "Invalid identity");
392            })?;
393
394        let target_uuid = idms_prox_read
395            .qs_read
396            .name_to_uuid(uuid_or_name.as_str())
397            .inspect_err(|err| {
398                error!(?err, "Error resolving id to target");
399            })?;
400
401        idms_prox_read
402            .qs_read
403            .scim_entry_id_get_ext(target_uuid, class, query, ident)
404    }
405
406    #[instrument(
407        level = "info",
408        skip_all,
409        fields(uuid = ?eventid)
410    )]
411    pub async fn scim_entry_search(
412        &self,
413        client_auth_info: ClientAuthInfo,
414        eventid: Uuid,
415        filter: ScimFilter,
416        query: ScimEntryGetQuery,
417    ) -> Result<ScimListResponse, OperationError> {
418        let ct = duration_from_epoch_now();
419        let mut idms_prox_read = self.idms.proxy_read().await?;
420        let ident = idms_prox_read
421            .validate_client_auth_info_to_ident(client_auth_info, ct)
422            .inspect_err(|err| {
423                error!(?err, "Invalid identity");
424            })?;
425
426        idms_prox_read.qs_read.scim_search_ext(ident, filter, query)
427    }
428}