kanidmd_core/actors/
v1_scim.rs

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