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(>e, 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}