kanidmd_core/actors/
v1_read.rs

1use std::convert::TryFrom;
2use std::fs;
3use std::net::IpAddr;
4use std::path::{Path, PathBuf};
5use std::str::FromStr;
6
7use kanidm_proto::backup::BackupCompression;
8use kanidm_proto::internal::{
9    ApiToken, AppLink, CURequest, CUSessionToken, CUStatus, CredentialStatus, IdentifyUserRequest,
10    IdentifyUserResponse, ImageValue, OperationError, RadiusAuthToken, SearchRequest,
11    SearchResponse, UserAuthToken,
12};
13use kanidm_proto::oauth2::OidcWebfingerResponse;
14use kanidm_proto::v1::{
15    AuthIssueSession, AuthRequest, Entry as ProtoEntry, UatStatus, UnixGroupToken, UnixUserToken,
16    WhoamiResponse,
17};
18use kanidmd_lib::idm::identityverification::{
19    IdentifyUserDisplayCodeEvent, IdentifyUserStartEvent, IdentifyUserSubmitCodeEvent,
20};
21use ldap3_proto::simple::*;
22use regex::Regex;
23use tracing::{error, info, instrument, trace};
24use uuid::Uuid;
25
26use compact_jwt::{JweCompact, Jwk, JwsCompact};
27
28use kanidmd_lib::be::BackendTransaction;
29use kanidmd_lib::prelude::*;
30use kanidmd_lib::{
31    event::{OnlineBackupEvent, SearchEvent, SearchResult, WhoamiResult},
32    filter::{Filter, FilterInvalid},
33    idm::account::ListUserAuthTokenEvent,
34    idm::credupdatesession::CredentialUpdateSessionToken,
35    idm::event::{
36        AuthEvent, AuthResult, CredentialStatusEvent, RadiusAuthTokenEvent, UnixGroupTokenEvent,
37        UnixUserAuthEvent, UnixUserTokenEvent,
38    },
39    idm::ldap::{LdapBoundToken, LdapResponseState},
40    idm::oauth2::{
41        AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AuthorisationRequest,
42        AuthoriseReject, AuthoriseResponse, JwkKeySet, Oauth2Error, Oauth2Rfc8414MetadataResponse,
43        OidcDiscoveryResponse, OidcToken,
44    },
45    idm::server::{DomainInfoRead, IdmServerTransaction},
46    idm::serviceaccount::ListApiTokenEvent,
47    idm::ClientAuthInfo,
48};
49
50use super::QueryServerReadV1;
51
52// ===========================================================
53
54impl QueryServerReadV1 {
55    // The server only receives "Message" structures, which
56    // are whole self contained DB operations with all parsing
57    // required complete. We still need to do certain validation steps, but
58    // at this point our just is just to route to do_<action>
59
60    // ! For uuid, we should deprecate `RequestExtensions::new_eventid` and just manually call
61    // ! `Uuid::new_v4().to_hyphenated().to_string()` instead of keeping a `Uuid` around.
62    // ! Ideally, this function takes &self, uat, req, and then a `uuid` argument that is a `&str` of the hyphenated uuid.
63    // ! Then we just don't skip uuid, and we don't have to do the custom `fields(..)` stuff in this macro call.
64    #[instrument(
65        level = "info",
66        name = "search",
67        skip_all,
68        fields(uuid = ?eventid)
69    )]
70    pub async fn handle_search(
71        &self,
72        client_auth_info: ClientAuthInfo,
73        req: SearchRequest,
74        eventid: Uuid,
75    ) -> Result<SearchResponse, OperationError> {
76        // Begin a read
77        let ct = duration_from_epoch_now();
78        let mut idms_prox_read = self.idms.proxy_read().await?;
79        let ident = idms_prox_read
80            .validate_client_auth_info_to_ident(client_auth_info, ct)
81            .map_err(|e| {
82                error!(?e, "Invalid identity");
83                e
84            })?;
85
86        // Make an event from the request
87        let search =
88            SearchEvent::from_message(ident, &req, &mut idms_prox_read.qs_read).map_err(|e| {
89                error!(?e, "Failed to begin search");
90                e
91            })?;
92
93        trace!(?search, "Begin event");
94
95        let entries = idms_prox_read.qs_read.search_ext(&search)?;
96
97        SearchResult::new(&mut idms_prox_read.qs_read, &entries).map(SearchResult::response)
98    }
99
100    #[instrument(
101        level = "info",
102        name = "auth",
103        skip_all,
104        fields(uuid = ?eventid)
105    )]
106    pub async fn handle_auth(
107        &self,
108        sessionid: Option<Uuid>,
109        req: AuthRequest,
110        eventid: Uuid,
111        client_auth_info: ClientAuthInfo,
112    ) -> Result<AuthResult, OperationError> {
113        // This is probably the first function that really implements logic
114        // "on top" of the db server concept. In this case we check if
115        // the credentials provided is sufficient to say if someone is
116        // "authenticated" or not.
117        let ct = duration_from_epoch_now();
118        let mut idm_auth = self.idms.auth().await?;
119        security_info!(?sessionid, ?req, "Begin auth event");
120
121        // Destructure it.
122        // Convert the AuthRequest to an AuthEvent that the idm server
123        // can use.
124        let ae = AuthEvent::from_message(sessionid, req).map_err(|e| {
125            error!(err = ?e, "Failed to parse AuthEvent");
126            e
127        })?;
128
129        // Trigger a session clean *before* we take any auth steps.
130        // It's important to do this before to ensure that timeouts on
131        // the session are enforced.
132        idm_auth.expire_auth_sessions(ct).await;
133
134        // Generally things like auth denied are in Ok() msgs
135        // so true errors should always trigger a rollback.
136        let res = idm_auth
137            .auth(&ae, ct, client_auth_info)
138            .await
139            .and_then(|r| idm_auth.commit().map(|_| r));
140
141        security_info!(?res, "Sending auth result");
142
143        res
144    }
145
146    #[instrument(
147        level = "info",
148        name = "reauth",
149        skip_all,
150        fields(uuid = ?eventid)
151    )]
152    pub async fn handle_reauth(
153        &self,
154        client_auth_info: ClientAuthInfo,
155        issue: AuthIssueSession,
156        eventid: Uuid,
157    ) -> Result<AuthResult, OperationError> {
158        let ct = duration_from_epoch_now();
159        let mut idm_auth = self.idms.auth().await?;
160        security_info!("Begin reauth event");
161
162        let ident = idm_auth
163            .validate_client_auth_info_to_ident(client_auth_info.clone(), ct)
164            .map_err(|e| {
165                error!(?e, "Invalid identity");
166                e
167            })?;
168
169        // Trigger a session clean *before* we take any auth steps.
170        // It's important to do this before to ensure that timeouts on
171        // the session are enforced.
172        idm_auth.expire_auth_sessions(ct).await;
173
174        // Generally things like auth denied are in Ok() msgs
175        // so true errors should always trigger a rollback.
176        let res = idm_auth
177            .reauth_init(ident, issue, ct, client_auth_info)
178            .await
179            .and_then(|r| idm_auth.commit().map(|_| r));
180
181        security_info!(?res, "Sending reauth result");
182
183        res
184    }
185
186    #[instrument(
187        level = "info",
188        name = "online_backup",
189        skip_all,
190        fields(uuid = ?msg.eventid)
191    )]
192    pub async fn handle_online_backup(
193        &self,
194        msg: OnlineBackupEvent,
195        outpath: &Path,
196        versions: usize,
197        compression: BackupCompression,
198    ) -> Result<(), OperationError> {
199        trace!(eventid = ?msg.eventid, "Begin online backup event");
200
201        let now = time::OffsetDateTime::now_utc();
202
203        #[allow(clippy::unwrap_used)]
204        let timestamp = now.format(&Rfc3339).unwrap();
205        let dest_file = outpath.join(format!("backup-{timestamp}.json{}", compression.suffix()));
206
207        if dest_file.exists() {
208            error!(
209                "Online backup file {} already exists, will not overwrite it.",
210                dest_file.display()
211            );
212            return Err(OperationError::InvalidState);
213        }
214
215        // Scope to limit the read txn.
216        {
217            let mut idms_prox_read = self.idms.proxy_read().await?;
218            idms_prox_read
219                .qs_read
220                .get_be_txn()
221                .backup(&dest_file, compression)
222                .map(|()| {
223                    info!("Online backup created {} successfully", dest_file.display());
224                })
225                .map_err(|e| {
226                    error!(
227                        "Online backup failed to create {}: {:?}",
228                        dest_file.display(),
229                        e
230                    );
231                    OperationError::InvalidState
232                })?;
233        }
234
235        // TODO: make the file rotation a separate function
236
237        // pattern to find automatically generated backup files
238        let re = Regex::new(r"^backup-\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?Z\.json")
239            .map_err(|error| {
240                error!(
241                    "Failed to parse regexp for online backup files: {:?}",
242                    error
243                );
244                OperationError::InvalidState
245            })?;
246
247        // cleanup of maximum backup versions to keep
248        let mut backup_file_list: Vec<PathBuf> = Vec::new();
249        // get a list of backup files
250        match fs::read_dir(outpath) {
251            Ok(rd) => {
252                for entry in rd {
253                    // get PathBuf
254                    let pb = entry
255                        .map_err(|e| {
256                            error!(?e, "Pathbuf access");
257                            OperationError::InvalidState
258                        })?
259                        .path();
260
261                    // skip everything that is not a file
262                    if !pb.is_file() {
263                        continue;
264                    }
265
266                    // get the /some/dir/<file_name> of the file
267                    let file_name = pb.file_name().and_then(|f| f.to_str()).ok_or_else(|| {
268                        error!("filename is invalid");
269                        OperationError::InvalidState
270                    })?;
271                    // check for a online backup file
272                    if re.is_match(file_name) {
273                        backup_file_list.push(pb.clone());
274                    }
275                }
276            }
277            Err(e) => {
278                error!(
279                    "Online backup cleanup error read dir {}: {}",
280                    outpath.display(),
281                    e
282                );
283                return Err(OperationError::InvalidState);
284            }
285        }
286
287        // sort it to have items listed old to new
288        backup_file_list.sort();
289
290        // Versions: OLD 10.9.8.7.6.5.4.3.2.1 NEW
291        //              |----delete----|keep|
292        // 10 items, we want to keep the latest 3
293
294        // if we have more files then we want to keep, me do some cleanup
295        if backup_file_list.len() > versions {
296            let x = backup_file_list.len() - versions;
297            info!(
298                "Online backup cleanup found {} versions, should keep {}, will remove {}",
299                backup_file_list.len(),
300                versions,
301                x
302            );
303            backup_file_list.truncate(x);
304
305            // removing files
306            for file in backup_file_list {
307                debug!("Online backup cleanup: removing {:?}", &file);
308                match fs::remove_file(&file) {
309                    Ok(_) => {}
310                    Err(e) => {
311                        error!(
312                            "Online backup cleanup failed to remove file {:?}: {:?}",
313                            file, e
314                        )
315                    }
316                };
317            }
318        } else {
319            debug!("Online backup cleanup had no files to remove");
320        };
321
322        Ok(())
323    }
324
325    #[instrument(
326        level = "info",
327        name = "whoami",
328        skip_all,
329        fields(uuid = ?eventid)
330    )]
331    pub async fn handle_whoami(
332        &self,
333        client_auth_info: ClientAuthInfo,
334        eventid: Uuid,
335    ) -> Result<WhoamiResponse, OperationError> {
336        // Begin a read
337        let ct = duration_from_epoch_now();
338        let mut idms_prox_read = self.idms.proxy_read().await?;
339        // Make an event from the whoami request. This will process the event and
340        // generate a selfuuid search.
341        //
342        // This current handles the unauthenticated check, and will
343        // trigger the failure, but if we can manage to work out async
344        // then move this to core.rs, and don't allow Option<UAT> to get
345        // this far.
346        let ident = idms_prox_read
347            .validate_client_auth_info_to_ident(client_auth_info, ct)
348            .map_err(|e| {
349                error!(?e, "Invalid identity");
350                e
351            })?;
352        let srch =
353            SearchEvent::from_whoami_request(ident, &idms_prox_read.qs_read).map_err(|e| {
354                error!(?e, "Failed to begin whoami");
355                e
356            })?;
357
358        trace!(search = ?srch, "Begin event");
359
360        let mut entries = idms_prox_read.qs_read.search_ext(&srch)?;
361
362        match entries.pop() {
363            Some(e) if entries.is_empty() => {
364                WhoamiResult::new(&mut idms_prox_read.qs_read, &e).map(WhoamiResult::response)
365            }
366            Some(_) => Err(OperationError::InvalidState), /* Somehow matched multiple entries... */
367            _ => Err(OperationError::NoMatchingEntries),
368        }
369    }
370
371    pub async fn pre_validate_client_auth_info(
372        &self,
373        client_auth_info: &mut ClientAuthInfo,
374    ) -> Result<(), OperationError> {
375        let ct = duration_from_epoch_now();
376        let mut idms_prox_read = self.idms.proxy_read().await?;
377        idms_prox_read
378            .pre_validate_client_auth_info(client_auth_info, ct)
379            .map_err(|e| {
380                error!(?e, "Invalid identity");
381                e
382            })
383    }
384
385    #[instrument(
386        level = "info",
387        name = "whoami_uat",
388        skip_all,
389        fields(uuid = ?eventid)
390    )]
391    pub async fn handle_whoami_uat(
392        &self,
393        client_auth_info: &ClientAuthInfo,
394        eventid: Uuid,
395    ) -> Result<UserAuthToken, OperationError> {
396        let ct = duration_from_epoch_now();
397        let mut idms_prox_read = self.idms.proxy_read().await?;
398        // Make an event from the whoami request. This will process the event and
399        // generate a selfuuid search.
400        //
401        // This current handles the unauthenticated check, and will
402        // trigger the failure, but if we can manage to work out async
403        // then move this to core.rs, and don't allow Option<UAT> to get
404        // this far.
405        idms_prox_read
406            .validate_client_auth_info_to_uat(client_auth_info, ct)
407            .map_err(|e| {
408                error!(?e, "Invalid identity");
409                e
410            })
411    }
412
413    #[instrument(level = "debug", skip_all)]
414    /// pull an image so we can present it to the user
415    pub async fn handle_oauth2_rs_image_get_image(
416        &self,
417        client_auth_info: ClientAuthInfo,
418        rs: Filter<FilterInvalid>,
419    ) -> Result<Option<ImageValue>, OperationError> {
420        let mut idms_prox_read = self.idms.proxy_read().await?;
421        let ct = duration_from_epoch_now();
422
423        let ident = idms_prox_read
424            .validate_client_auth_info_to_ident(client_auth_info, ct)
425            .map_err(|e| {
426                error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_get_image");
427                e
428            })?;
429        let attrs = vec![Attribute::Image.to_string()];
430
431        let search = SearchEvent::from_internal_message(
432            ident,
433            &rs,
434            Some(attrs.as_slice()),
435            &mut idms_prox_read.qs_read,
436        )?;
437
438        let entries = idms_prox_read.qs_read.search(&search)?;
439        Ok(entries
440            .first()
441            .and_then(|entry| entry.get_ava_single_image(Attribute::Image)))
442    }
443
444    #[instrument(
445        level = "info",
446        skip_all,
447        fields(uuid = ?eventid)
448    )]
449    pub async fn handle_internalsearch(
450        &self,
451        client_auth_info: ClientAuthInfo,
452        filter: Filter<FilterInvalid>,
453        attrs: Option<Vec<String>>,
454        eventid: Uuid,
455    ) -> Result<Vec<ProtoEntry>, OperationError> {
456        let ct = duration_from_epoch_now();
457        let mut idms_prox_read = self.idms.proxy_read().await?;
458        let ident = idms_prox_read
459            .validate_client_auth_info_to_ident(client_auth_info, ct)
460            .map_err(|e| {
461                error!("Invalid identity: {:?}", e);
462                e
463            })?;
464        // Make an event from the request
465        let srch = match SearchEvent::from_internal_message(
466            ident,
467            &filter,
468            attrs.as_deref(),
469            &mut idms_prox_read.qs_read,
470        ) {
471            Ok(s) => s,
472            Err(e) => {
473                error!("Failed to begin internal api search: {:?}", e);
474                return Err(e);
475            }
476        };
477
478        trace!(?srch, "Begin event");
479
480        match idms_prox_read.qs_read.search_ext(&srch) {
481            Ok(entries) => SearchResult::new(&mut idms_prox_read.qs_read, &entries)
482                .map(|ok_sr| ok_sr.into_proto_array()),
483            Err(e) => Err(e),
484        }
485    }
486
487    #[instrument(
488        level = "info",
489        skip_all,
490        fields(uuid = ?eventid)
491    )]
492    pub async fn handle_search_refers(
493        &self,
494        client_auth_info: ClientAuthInfo,
495        filter: Filter<FilterInvalid>,
496        uuid_or_name: String,
497        attrs: Option<Vec<String>>,
498        eventid: Uuid,
499    ) -> Result<Vec<ProtoEntry>, OperationError> {
500        let ct = duration_from_epoch_now();
501        let mut idms_prox_read = self.idms.proxy_read().await?;
502        let ident = idms_prox_read
503            .validate_client_auth_info_to_ident(client_auth_info, ct)
504            .map_err(|e| {
505                error!("Invalid identity: {:?}", e);
506                e
507            })?;
508
509        let target_uuid = idms_prox_read
510            .qs_read
511            .name_to_uuid(uuid_or_name.as_str())
512            .inspect_err(|err| {
513                error!(?err, "Error resolving id to target");
514            })?;
515
516        // Update the filter with the target_uuid
517        let filter = Filter::join_parts_and(
518            filter,
519            filter_all!(f_eq(Attribute::Refers, PartialValue::Refer(target_uuid))),
520        );
521
522        // Make an event from the request
523        let srch = match SearchEvent::from_internal_message(
524            ident,
525            &filter,
526            attrs.as_deref(),
527            &mut idms_prox_read.qs_read,
528        ) {
529            Ok(s) => s,
530            Err(e) => {
531                error!("Failed to begin internal api search: {:?}", e);
532                return Err(e);
533            }
534        };
535
536        trace!(?srch, "Begin event");
537
538        match idms_prox_read.qs_read.search_ext(&srch) {
539            Ok(entries) => SearchResult::new(&mut idms_prox_read.qs_read, &entries)
540                .map(|ok_sr| ok_sr.into_proto_array()),
541            Err(e) => Err(e),
542        }
543    }
544
545    #[instrument(
546        level = "info",
547        skip_all,
548        fields(uuid = ?eventid)
549    )]
550    pub async fn handle_internalsearchrecycled(
551        &self,
552        client_auth_info: ClientAuthInfo,
553        filter: Filter<FilterInvalid>,
554        attrs: Option<Vec<String>>,
555        eventid: Uuid,
556    ) -> Result<Vec<ProtoEntry>, OperationError> {
557        let ct = duration_from_epoch_now();
558        let mut idms_prox_read = self.idms.proxy_read().await?;
559
560        let ident = idms_prox_read
561            .validate_client_auth_info_to_ident(client_auth_info, ct)
562            .map_err(|e| {
563                error!("Invalid identity: {:?}", e);
564                e
565            })?;
566        // Make an event from the request
567        let srch = match SearchEvent::from_internal_recycle_message(
568            ident,
569            &filter,
570            attrs.as_deref(),
571            &idms_prox_read.qs_read,
572        ) {
573            Ok(s) => s,
574            Err(e) => {
575                error!("Failed to begin recycled search: {:?}", e);
576                return Err(e);
577            }
578        };
579
580        trace!(?srch, "Begin event");
581
582        match idms_prox_read.qs_read.search_ext(&srch) {
583            Ok(entries) => SearchResult::new(&mut idms_prox_read.qs_read, &entries)
584                .map(|ok_sr| ok_sr.into_proto_array()),
585            Err(e) => Err(e),
586        }
587    }
588
589    #[instrument(
590        level = "info",
591        skip_all,
592        fields(uuid = ?eventid)
593    )]
594    pub async fn handle_internalradiusread(
595        &self,
596        client_auth_info: ClientAuthInfo,
597        uuid_or_name: String,
598        eventid: Uuid,
599    ) -> Result<Option<String>, OperationError> {
600        let ct = duration_from_epoch_now();
601        let mut idms_prox_read = self.idms.proxy_read().await?;
602        let ident = idms_prox_read
603            .validate_client_auth_info_to_ident(client_auth_info, ct)
604            .map_err(|e| {
605                error!("Invalid identity: {:?}", e);
606                e
607            })?;
608
609        let target_uuid = idms_prox_read
610            .qs_read
611            .name_to_uuid(uuid_or_name.as_str())
612            .inspect_err(|err| {
613                error!(?err, "Error resolving id to target");
614            })?;
615
616        // Make an event from the request
617        let srch = match SearchEvent::from_target_uuid_request(
618            ident,
619            target_uuid,
620            &idms_prox_read.qs_read,
621        ) {
622            Ok(s) => s,
623            Err(e) => {
624                error!("Failed to begin radius read: {:?}", e);
625                return Err(e);
626            }
627        };
628
629        trace!(?srch, "Begin event");
630
631        // We have to use search_ext to guarantee acs was applied.
632        match idms_prox_read.qs_read.search_ext(&srch) {
633            Ok(mut entries) => {
634                let r = entries
635                    .pop()
636                    // From the entry, turn it into the value
637                    .and_then(|entry| {
638                        entry
639                            .get_ava_single(Attribute::RadiusSecret)
640                            .and_then(|v| v.get_secret_str().map(str::to_string))
641                    });
642                Ok(r)
643            }
644            Err(e) => Err(e),
645        }
646    }
647
648    #[instrument(
649        level = "info",
650        skip_all,
651        fields(uuid = ?eventid)
652    )]
653    pub async fn handle_internalradiustokenread(
654        &self,
655        client_auth_info: ClientAuthInfo,
656        uuid_or_name: String,
657        eventid: Uuid,
658    ) -> Result<RadiusAuthToken, OperationError> {
659        let ct = duration_from_epoch_now();
660        let mut idms_prox_read = self.idms.proxy_read().await?;
661
662        let ident = idms_prox_read
663            .validate_client_auth_info_to_ident(client_auth_info, ct)
664            .map_err(|e| {
665                error!("Invalid identity: {:?}", e);
666                e
667            })?;
668
669        let target_uuid = idms_prox_read
670            .qs_read
671            .name_to_uuid(uuid_or_name.as_str())
672            .inspect_err(|err| {
673                error!(?err, "Error resolving id to target");
674            })?;
675
676        // Make an event from the request
677        let rate = match RadiusAuthTokenEvent::from_parts(
678            // &idms_prox_read.qs_read,
679            ident,
680            target_uuid,
681        ) {
682            Ok(s) => s,
683            Err(e) => {
684                error!("Failed to begin radius token read: {:?}", e);
685                return Err(e);
686            }
687        };
688
689        trace!(?rate, "Begin event");
690
691        idms_prox_read.get_radiusauthtoken(&rate, ct)
692    }
693
694    #[instrument(
695        level = "info",
696        skip_all,
697        fields(uuid = ?eventid)
698    )]
699    pub async fn handle_internalunixusertokenread(
700        &self,
701        client_auth_info: ClientAuthInfo,
702        uuid_or_name: String,
703        eventid: Uuid,
704    ) -> Result<UnixUserToken, OperationError> {
705        let ct = duration_from_epoch_now();
706        let mut idms_prox_read = self.idms.proxy_read().await?;
707
708        let ident = idms_prox_read
709            .validate_client_auth_info_to_ident(client_auth_info, ct)
710            .map_err(|e| {
711                error!("Invalid identity: {:?}", e);
712                e
713            })?;
714
715        let target_uuid = idms_prox_read
716            .qs_read
717            .name_to_uuid(uuid_or_name.as_str())
718            .map_err(|e| {
719                // sometimes it comes back as empty which is bad, it's safe to start with `<empty` here
720                // because a valid username/uuid can never start with that and we're only logging it
721                let uuid_or_name_val = match uuid_or_name.is_empty() {
722                    true => "<empty uuid_or_name>",
723                    false => &uuid_or_name,
724                };
725                admin_info!(
726                    err = ?e,
727                    "Error resolving {} as gidnumber continuing ...",
728                    uuid_or_name_val
729                );
730                e
731            })?;
732
733        // Make an event from the request
734        let rate = match UnixUserTokenEvent::from_parts(ident, target_uuid) {
735            Ok(s) => s,
736            Err(e) => {
737                error!("Failed to begin unix token read: {:?}", e);
738                return Err(e);
739            }
740        };
741
742        trace!(?rate, "Begin event");
743
744        idms_prox_read.get_unixusertoken(&rate, ct)
745    }
746
747    #[instrument(
748        level = "info",
749        skip_all,
750        fields(uuid = ?eventid)
751    )]
752    pub async fn handle_internalunixgrouptokenread(
753        &self,
754        client_auth_info: ClientAuthInfo,
755        uuid_or_name: String,
756        eventid: Uuid,
757    ) -> Result<UnixGroupToken, OperationError> {
758        let ct = duration_from_epoch_now();
759        let mut idms_prox_read = self.idms.proxy_read().await?;
760        let ident = idms_prox_read
761            .validate_client_auth_info_to_ident(client_auth_info, ct)
762            .map_err(|e| {
763                error!("Invalid identity: {:?}", e);
764                e
765            })?;
766
767        let target_uuid = idms_prox_read
768            .qs_read
769            .name_to_uuid(uuid_or_name.as_str())
770            .map_err(|e| {
771                admin_info!(err = ?e, "Error resolving as gidnumber continuing");
772                e
773            })?;
774
775        // Make an event from the request
776        let rate = match UnixGroupTokenEvent::from_parts(
777            // &idms_prox_read.qs_read,
778            ident,
779            target_uuid,
780        ) {
781            Ok(s) => s,
782            Err(e) => {
783                error!("Failed to begin unix group token read: {:?}", e);
784                return Err(e);
785            }
786        };
787
788        trace!(?rate, "Begin event");
789
790        idms_prox_read.get_unixgrouptoken(&rate)
791    }
792
793    #[instrument(
794        level = "info",
795        skip_all,
796        fields(uuid = ?eventid)
797    )]
798    pub async fn handle_internalsshkeyread(
799        &self,
800        client_auth_info: ClientAuthInfo,
801        uuid_or_name: String,
802        eventid: Uuid,
803    ) -> Result<Vec<String>, OperationError> {
804        let ct = duration_from_epoch_now();
805        let mut idms_prox_read = self.idms.proxy_read().await?;
806        let ident = idms_prox_read
807            .validate_client_auth_info_to_ident(client_auth_info, ct)
808            .map_err(|e| {
809                error!("Invalid identity: {:?}", e);
810                e
811            })?;
812        let target_uuid = idms_prox_read
813            .qs_read
814            .name_to_uuid(uuid_or_name.as_str())
815            .inspect_err(|err| {
816                error!(?err, "Error resolving id to target");
817            })?;
818
819        // Make an event from the request
820        let srch = match SearchEvent::from_target_uuid_request(
821            ident,
822            target_uuid,
823            &idms_prox_read.qs_read,
824        ) {
825            Ok(s) => s,
826            Err(e) => {
827                error!("Failed to begin ssh key read: {:?}", e);
828                return Err(e);
829            }
830        };
831
832        trace!(?srch, "Begin event");
833
834        match idms_prox_read.qs_read.search_ext(&srch) {
835            Ok(mut entries) => {
836                let r = entries
837                    .pop()
838                    // get the first entry
839                    .and_then(|e| {
840                        // From the entry, turn it into the value
841                        e.get_ava_iter_sshpubkeys(Attribute::SshPublicKey)
842                            .map(|i| i.collect())
843                    })
844                    .unwrap_or_else(|| {
845                        // No matching entry? Return none.
846                        Vec::new()
847                    });
848                Ok(r)
849            }
850            Err(e) => Err(e),
851        }
852    }
853
854    #[instrument(
855        level = "info",
856        skip_all,
857        fields(uuid = ?eventid)
858    )]
859    pub async fn handle_internalsshkeytagread(
860        &self,
861        client_auth_info: ClientAuthInfo,
862        uuid_or_name: String,
863        tag: String,
864        eventid: Uuid,
865    ) -> Result<Option<String>, OperationError> {
866        let ct = duration_from_epoch_now();
867        let mut idms_prox_read = self.idms.proxy_read().await?;
868        let ident = idms_prox_read
869            .validate_client_auth_info_to_ident(client_auth_info, ct)
870            .map_err(|e| {
871                error!("Invalid identity: {:?}", e);
872                e
873            })?;
874        let target_uuid = idms_prox_read
875            .qs_read
876            .name_to_uuid(uuid_or_name.as_str())
877            .inspect_err(|err| {
878                admin_info!(?err, "Error resolving id to target");
879            })?;
880
881        // Make an event from the request
882        let srch = match SearchEvent::from_target_uuid_request(
883            ident,
884            target_uuid,
885            &idms_prox_read.qs_read,
886        ) {
887            Ok(s) => s,
888            Err(e) => {
889                error!("Failed to begin sshkey tag read: {:?}", e);
890                return Err(e);
891            }
892        };
893
894        trace!(?srch, "Begin event");
895
896        match idms_prox_read.qs_read.search_ext(&srch) {
897            Ok(mut entries) => {
898                let r = entries
899                    .pop()
900                    // get the first entry
901                    .map(|e| {
902                        // From the entry, turn it into the value
903                        e.get_ava_set(Attribute::SshPublicKey).and_then(|vs| {
904                            // Get the one tagged value
905                            vs.get_ssh_tag(&tag).map(|pk| pk.to_string())
906                        })
907                    })
908                    .unwrap_or_else(|| {
909                        // No matching entry? Return none.
910                        None
911                    });
912                Ok(r)
913            }
914            Err(e) => Err(e),
915        }
916    }
917
918    #[instrument(
919        level = "info",
920        skip_all,
921        fields(uuid = ?eventid)
922    )]
923    pub async fn handle_service_account_api_token_get(
924        &self,
925        client_auth_info: ClientAuthInfo,
926        uuid_or_name: String,
927        eventid: Uuid,
928    ) -> Result<Vec<ApiToken>, OperationError> {
929        let ct = duration_from_epoch_now();
930        let mut idms_prox_read = self.idms.proxy_read().await?;
931        let ident = idms_prox_read
932            .validate_client_auth_info_to_ident(client_auth_info, ct)
933            .map_err(|e| {
934                error!("Invalid identity: {:?}", e);
935                e
936            })?;
937        let target = idms_prox_read
938            .qs_read
939            .name_to_uuid(uuid_or_name.as_str())
940            .inspect_err(|err| {
941                error!(?err, "Error resolving id to target");
942            })?;
943
944        let lte = ListApiTokenEvent { ident, target };
945
946        idms_prox_read.service_account_list_api_token(&lte)
947    }
948
949    #[instrument(
950        level = "info",
951        skip_all,
952        fields(uuid = ?eventid)
953    )]
954    pub async fn handle_account_user_auth_token_get(
955        &self,
956        client_auth_info: ClientAuthInfo,
957        uuid_or_name: String,
958        eventid: Uuid,
959    ) -> Result<Vec<UatStatus>, OperationError> {
960        let ct = duration_from_epoch_now();
961        let mut idms_prox_read = self.idms.proxy_read().await?;
962        let ident = idms_prox_read
963            .validate_client_auth_info_to_ident(client_auth_info, ct)
964            .map_err(|e| {
965                error!("Invalid identity: {:?}", e);
966                e
967            })?;
968        let target = idms_prox_read
969            .qs_read
970            .name_to_uuid(uuid_or_name.as_str())
971            .inspect_err(|err| {
972                error!(?err, "Error resolving id to target");
973            })?;
974
975        let lte = ListUserAuthTokenEvent { ident, target };
976
977        idms_prox_read.account_list_user_auth_tokens(&lte)
978    }
979
980    #[instrument(
981        level = "info",
982        skip_all,
983        fields(uuid = ?eventid)
984    )]
985    pub async fn handle_user_identity_verification(
986        &self,
987        client_auth_info: ClientAuthInfo,
988        eventid: Uuid,
989        user_request: IdentifyUserRequest,
990        other_id: String,
991    ) -> Result<IdentifyUserResponse, OperationError> {
992        trace!("{:?}", &user_request);
993        let ct = duration_from_epoch_now();
994        let mut idms_prox_read = self.idms.proxy_read().await?;
995        let ident = idms_prox_read
996            .validate_client_auth_info_to_ident(client_auth_info, ct)
997            .map_err(|e| {
998                error!("Invalid identity: {:?}", e);
999                e
1000            })?;
1001        let target = idms_prox_read
1002            .qs_read
1003            .name_to_uuid(&other_id)
1004            .map_err(|e| {
1005                error!("No user found with the provided ID: {:?}", e);
1006                e
1007            })?;
1008        match user_request {
1009            IdentifyUserRequest::Start => idms_prox_read
1010                .handle_identify_user_start(&IdentifyUserStartEvent::new(target, ident), ct),
1011            IdentifyUserRequest::DisplayCode => idms_prox_read.handle_identify_user_display_code(
1012                &IdentifyUserDisplayCodeEvent::new(target, ident),
1013                ct,
1014            ),
1015            IdentifyUserRequest::SubmitCode { other_totp } => idms_prox_read
1016                .handle_identify_user_submit_code(
1017                    &IdentifyUserSubmitCodeEvent::new(target, ident, other_totp),
1018                    ct,
1019                ),
1020        }
1021    }
1022
1023    #[instrument(
1024        level = "info",
1025        skip_all,
1026        fields(uuid = ?eventid)
1027    )]
1028    pub async fn handle_idmaccountunixauth(
1029        &self,
1030        client_auth_info: ClientAuthInfo,
1031        uuid_or_name: String,
1032        cred: String,
1033        eventid: Uuid,
1034    ) -> Result<Option<UnixUserToken>, OperationError> {
1035        let ct = duration_from_epoch_now();
1036        let mut idm_auth = self.idms.auth().await?;
1037        // resolve the id
1038        let ident = idm_auth
1039            .validate_client_auth_info_to_ident(client_auth_info, ct)
1040            .map_err(|e| {
1041                error!(err = ?e, "Invalid identity");
1042                e
1043            })?;
1044
1045        let target_uuid = idm_auth
1046            .qs_read
1047            .name_to_uuid(uuid_or_name.as_str())
1048            .map_err(|e| {
1049                admin_info!(err = ?e, "Error resolving as gidnumber continuing");
1050                e
1051            })?;
1052        // Make an event from the request
1053        let uuae = match UnixUserAuthEvent::from_parts(ident, target_uuid, cred) {
1054            Ok(s) => s,
1055            Err(e) => {
1056                error!(err = ?e, "Failed to begin unix auth");
1057                return Err(e);
1058            }
1059        };
1060
1061        security_info!(event = ?uuae, "Begin unix auth event");
1062
1063        let res = idm_auth
1064            .auth_unix(&uuae, ct)
1065            .await
1066            .and_then(|r| idm_auth.commit().map(|_| r));
1067
1068        security_info!(?res, "Sending result");
1069
1070        res
1071    }
1072
1073    #[instrument(
1074        level = "info",
1075        skip_all,
1076        fields(uuid = ?eventid)
1077    )]
1078    pub async fn handle_idmcredentialstatus(
1079        &self,
1080        client_auth_info: ClientAuthInfo,
1081        uuid_or_name: String,
1082        eventid: Uuid,
1083    ) -> Result<CredentialStatus, OperationError> {
1084        let ct = duration_from_epoch_now();
1085        let mut idms_prox_read = self.idms.proxy_read().await?;
1086
1087        let ident = idms_prox_read
1088            .validate_client_auth_info_to_ident(client_auth_info, ct)
1089            .map_err(|e| {
1090                error!(err = ?e, "Invalid identity");
1091                e
1092            })?;
1093        let target_uuid = idms_prox_read
1094            .qs_read
1095            .name_to_uuid(uuid_or_name.as_str())
1096            .map_err(|e| {
1097                error!(err = ?e, "Error resolving id to target");
1098                e
1099            })?;
1100
1101        // Make an event from the request
1102        let cse = match CredentialStatusEvent::from_parts(
1103            // &idms_prox_read.qs_read,
1104            ident,
1105            target_uuid,
1106        ) {
1107            Ok(s) => s,
1108            Err(e) => {
1109                error!(err = ?e, "Failed to begin credential status read");
1110                return Err(e);
1111            }
1112        };
1113
1114        trace!(?cse, "Begin event");
1115
1116        idms_prox_read.get_credentialstatus(&cse)
1117    }
1118
1119    #[instrument(
1120        level = "info",
1121        skip_all,
1122        fields(uuid = ?eventid)
1123    )]
1124    pub async fn handle_idmcredentialupdatestatus(
1125        &self,
1126        session_token: CUSessionToken,
1127        eventid: Uuid,
1128    ) -> Result<CUStatus, OperationError> {
1129        let session_token = JweCompact::from_str(&session_token.token)
1130            .map(|token_enc| CredentialUpdateSessionToken { token_enc })
1131            .map_err(|err| {
1132                error!(?err, "malformed token");
1133                OperationError::InvalidRequestState
1134            })?;
1135
1136        // Don't proceed unless the token parses
1137        let ct = duration_from_epoch_now();
1138        let idms_cred_update = self.idms.cred_update_transaction().await?;
1139
1140        idms_cred_update
1141            .credential_update_status(&session_token, ct)
1142            .map_err(|e| {
1143                error!(
1144                    err = ?e,
1145                    "Failed to begin credential_update_status",
1146                );
1147                e
1148            })
1149            .map(|sta| sta.into())
1150    }
1151
1152    #[instrument(
1153        level = "info",
1154        skip_all,
1155        fields(uuid = ?eventid)
1156    )]
1157    pub async fn handle_idmcredentialupdate(
1158        &self,
1159        session_token: CUSessionToken,
1160        scr: CURequest,
1161        eventid: Uuid,
1162    ) -> Result<CUStatus, OperationError> {
1163        let session_token = JweCompact::from_str(&session_token.token)
1164            .map(|token_enc| CredentialUpdateSessionToken { token_enc })
1165            .map_err(|err| {
1166                error!(?err, "Invalid Token - Must be a compact JWE");
1167                OperationError::InvalidRequestState
1168            })?;
1169
1170        let ct = duration_from_epoch_now();
1171        let idms_cred_update = self.idms.cred_update_transaction().await?;
1172
1173        debug!(?scr);
1174
1175        match scr {
1176            CURequest::PrimaryRemove => idms_cred_update
1177                .credential_primary_delete(&session_token, ct)
1178                .inspect_err(|err| {
1179                    error!(?err, "Failed to begin credential_primary_delete",);
1180                }),
1181            CURequest::PasswordQualityCheck(pw) => idms_cred_update
1182                .credential_check_password_quality(&session_token, ct, &pw)
1183                .inspect_err(|err| {
1184                    error!(?err, "Failed to begin credential_check_password_quality",);
1185                }),
1186            CURequest::Password(pw) => idms_cred_update
1187                .credential_primary_set_password(&session_token, ct, &pw)
1188                .inspect_err(|err| {
1189                    error!(?err, "Failed to begin credential_primary_set_password",);
1190                }),
1191            CURequest::CancelMFAReg => idms_cred_update
1192                .credential_update_cancel_mfareg(&session_token, ct)
1193                .inspect_err(|err| {
1194                    error!(?err, "Failed to begin credential_update_cancel_mfareg",);
1195                }),
1196            CURequest::TotpGenerate => idms_cred_update
1197                .credential_primary_init_totp(&session_token, ct)
1198                .inspect_err(|err| {
1199                    error!(?err, "Failed to begin credential_primary_init_totp",);
1200                }),
1201            CURequest::TotpVerify(totp_chal, label) => idms_cred_update
1202                .credential_primary_check_totp(&session_token, ct, totp_chal, &label)
1203                .inspect_err(|err| {
1204                    error!(?err, "Failed to begin credential_primary_check_totp",);
1205                }),
1206            CURequest::TotpAcceptSha1 => idms_cred_update
1207                .credential_primary_accept_sha1_totp(&session_token, ct)
1208                .inspect_err(|err| {
1209                    error!(?err, "Failed to begin credential_primary_accept_sha1_totp",);
1210                }),
1211            CURequest::TotpRemove(label) => idms_cred_update
1212                .credential_primary_remove_totp(&session_token, ct, &label)
1213                .inspect_err(|err| {
1214                    error!(?err, "Failed to begin credential_primary_remove_totp",);
1215                }),
1216            CURequest::BackupCodeGenerate => idms_cred_update
1217                .credential_primary_init_backup_codes(&session_token, ct)
1218                .inspect_err(|err| {
1219                    error!(?err, "Failed to begin credential_primary_init_backup_codes",);
1220                }),
1221            CURequest::BackupCodeRemove => idms_cred_update
1222                .credential_primary_remove_backup_codes(&session_token, ct)
1223                .inspect_err(|err| {
1224                    error!(
1225                        ?err,
1226                        "Failed to begin credential_primary_remove_backup_codes",
1227                    );
1228                }),
1229            CURequest::PasskeyInit => idms_cred_update
1230                .credential_passkey_init(&session_token, ct)
1231                .inspect_err(|err| {
1232                    error!(?err, "Failed to begin credential_passkey_init",);
1233                }),
1234            CURequest::PasskeyFinish(label, rpkc) => idms_cred_update
1235                .credential_passkey_finish(&session_token, ct, label, &rpkc)
1236                .inspect_err(|err| {
1237                    error!(?err, "Failed to begin credential_passkey_finish",);
1238                }),
1239            CURequest::PasskeyRemove(uuid) => idms_cred_update
1240                .credential_passkey_remove(&session_token, ct, uuid)
1241                .inspect_err(|err| {
1242                    error!(?err, "Failed to begin credential_passkey_remove",);
1243                }),
1244            CURequest::AttestedPasskeyInit => idms_cred_update
1245                .credential_attested_passkey_init(&session_token, ct)
1246                .inspect_err(|err| {
1247                    error!(?err, "Failed to begin credential_attested_passkey_init",);
1248                }),
1249            CURequest::AttestedPasskeyFinish(label, rpkc) => idms_cred_update
1250                .credential_attested_passkey_finish(&session_token, ct, label, &rpkc)
1251                .inspect_err(|err| {
1252                    error!(?err, "Failed to begin credential_attested_passkey_finish",);
1253                }),
1254            CURequest::AttestedPasskeyRemove(uuid) => idms_cred_update
1255                .credential_attested_passkey_remove(&session_token, ct, uuid)
1256                .inspect_err(|err| {
1257                    error!(?err, "Failed to begin credential_attested_passkey_remove",);
1258                }),
1259            CURequest::UnixPasswordRemove => idms_cred_update
1260                .credential_unix_delete(&session_token, ct)
1261                .inspect_err(|err| {
1262                    error!(?err, "Failed to begin credential_unix_delete");
1263                }),
1264            CURequest::UnixPassword(pw) => idms_cred_update
1265                .credential_unix_set_password(&session_token, ct, &pw)
1266                .inspect_err(|err| {
1267                    error!(?err, "Failed to begin credential_unix_set_password");
1268                }),
1269            CURequest::SshPublicKey(label, pubkey) => idms_cred_update
1270                .credential_sshkey_add(&session_token, ct, label, pubkey)
1271                .inspect_err(|err| {
1272                    error!(?err, "Failed to begin credential_sshkey_remove");
1273                }),
1274            CURequest::SshPublicKeyRemove(label) => idms_cred_update
1275                .credential_sshkey_remove(&session_token, ct, &label)
1276                .inspect_err(|err| {
1277                    error!(?err, "Failed to begin credential_sshkey_remove");
1278                }),
1279        }
1280        .map(|sta| sta.into())
1281    }
1282
1283    #[instrument(
1284        level = "info",
1285        skip_all,
1286        fields(uuid = ?eventid)
1287    )]
1288    pub async fn handle_oauth2_basic_secret_read(
1289        &self,
1290        client_auth_info: ClientAuthInfo,
1291        filter: Filter<FilterInvalid>,
1292        eventid: Uuid,
1293    ) -> Result<Option<String>, OperationError> {
1294        let ct = duration_from_epoch_now();
1295        let mut idms_prox_read = self.idms.proxy_read().await?;
1296        let ident = idms_prox_read
1297            .validate_client_auth_info_to_ident(client_auth_info, ct)
1298            .map_err(|e| {
1299                error!("Invalid identity: {:?}", e);
1300                e
1301            })?;
1302
1303        // Make an event from the request
1304        let srch = match SearchEvent::from_internal_message(
1305            ident,
1306            &filter,
1307            None,
1308            &mut idms_prox_read.qs_read,
1309        ) {
1310            Ok(s) => s,
1311            Err(e) => {
1312                error!("Failed to begin oauth2 basic secret read: {:?}", e);
1313                return Err(e);
1314            }
1315        };
1316
1317        trace!(?srch, "Begin event");
1318
1319        // We have to use search_ext to guarantee acs was applied.
1320        match idms_prox_read.qs_read.search_ext(&srch) {
1321            Ok(mut entries) => {
1322                let r = entries
1323                    .pop()
1324                    // From the entry, turn it into the value
1325                    .and_then(|entry| {
1326                        entry
1327                            .get_ava_single(Attribute::OAuth2RsBasicSecret)
1328                            .and_then(|v| v.get_secret_str().map(str::to_string))
1329                    });
1330                Ok(r)
1331            }
1332            Err(e) => Err(e),
1333        }
1334    }
1335
1336    #[instrument(
1337        level = "info",
1338        skip_all,
1339        fields(uuid = ?eventid)
1340    )]
1341    pub async fn handle_oauth2_authorise(
1342        &self,
1343        client_auth_info: ClientAuthInfo,
1344        auth_req: AuthorisationRequest,
1345        eventid: Uuid,
1346    ) -> Result<AuthoriseResponse, Oauth2Error> {
1347        let ct = duration_from_epoch_now();
1348        let mut idms_prox_read = self
1349            .idms
1350            .proxy_read()
1351            .await
1352            .map_err(Oauth2Error::ServerError)?;
1353        let ident = idms_prox_read
1354            .validate_client_auth_info_to_ident(client_auth_info, ct)
1355            .inspect_err(|e| {
1356                error!("Invalid identity: {:?}", e);
1357            })
1358            .ok();
1359
1360        // Now we can send to the idm server for authorisation checking.
1361        idms_prox_read.check_oauth2_authorisation(ident.as_ref(), &auth_req, ct)
1362    }
1363
1364    #[instrument(
1365        level = "info",
1366        skip_all,
1367        fields(uuid = ?eventid)
1368    )]
1369    pub async fn handle_oauth2_authorise_reject(
1370        &self,
1371        client_auth_info: ClientAuthInfo,
1372        consent_req: String,
1373        eventid: Uuid,
1374    ) -> Result<AuthoriseReject, OperationError> {
1375        let ct = duration_from_epoch_now();
1376        let mut idms_prox_read = self.idms.proxy_read().await?;
1377        let ident = idms_prox_read
1378            .validate_client_auth_info_to_ident(client_auth_info, ct)
1379            .map_err(|e| {
1380                error!("Invalid identity: {:?}", e);
1381                e
1382            })?;
1383
1384        idms_prox_read.check_oauth2_authorise_reject(&ident, &consent_req, ct)
1385    }
1386
1387    #[instrument(
1388        level = "info",
1389        skip_all,
1390        fields(uuid = ?eventid)
1391    )]
1392    pub async fn handle_oauth2_token_introspect(
1393        &self,
1394        client_auth_info: ClientAuthInfo,
1395        intr_req: AccessTokenIntrospectRequest,
1396        eventid: Uuid,
1397    ) -> Result<AccessTokenIntrospectResponse, Oauth2Error> {
1398        let ct = duration_from_epoch_now();
1399        let mut idms_prox_read = self
1400            .idms
1401            .proxy_read()
1402            .await
1403            .map_err(Oauth2Error::ServerError)?;
1404        // Now we can send to the idm server for introspection checking.
1405        idms_prox_read.check_oauth2_token_introspect(&client_auth_info, &intr_req, ct)
1406    }
1407
1408    #[instrument(
1409        level = "info",
1410        skip_all,
1411        fields(uuid = ?eventid)
1412    )]
1413    pub async fn handle_oauth2_openid_userinfo(
1414        &self,
1415        client_id: String,
1416        token: &JwsCompact,
1417        eventid: Uuid,
1418    ) -> Result<OidcToken, Oauth2Error> {
1419        let ct = duration_from_epoch_now();
1420        let mut idms_prox_read = self
1421            .idms
1422            .proxy_read()
1423            .await
1424            .map_err(Oauth2Error::ServerError)?;
1425        idms_prox_read.oauth2_openid_userinfo(&client_id, token, ct)
1426    }
1427
1428    #[instrument(
1429        level = "info",
1430        skip_all,
1431        fields(uuid = ?eventid)
1432    )]
1433    pub async fn handle_oauth2_openid_discovery(
1434        &self,
1435        client_id: String,
1436        eventid: Uuid,
1437    ) -> Result<OidcDiscoveryResponse, OperationError> {
1438        let idms_prox_read = self.idms.proxy_read().await?;
1439        idms_prox_read.oauth2_openid_discovery(&client_id)
1440    }
1441
1442    #[instrument(
1443        level = "info",
1444        skip_all,
1445        fields(uuid = ?eventid)
1446    )]
1447    pub async fn handle_oauth2_webfinger_discovery(
1448        &self,
1449        client_id: &str,
1450        resource_id: &str,
1451        eventid: Uuid,
1452    ) -> Result<OidcWebfingerResponse, OperationError> {
1453        let mut idms_prox_read = self.idms.proxy_read().await?;
1454        idms_prox_read.oauth2_openid_webfinger(client_id, resource_id)
1455    }
1456
1457    #[instrument(
1458        level = "info",
1459        skip_all,
1460        fields(uuid = ?eventid)
1461    )]
1462    pub async fn handle_oauth2_rfc8414_metadata(
1463        &self,
1464        client_id: String,
1465        eventid: Uuid,
1466    ) -> Result<Oauth2Rfc8414MetadataResponse, OperationError> {
1467        let idms_prox_read = self.idms.proxy_read().await?;
1468        idms_prox_read.oauth2_rfc8414_metadata(&client_id)
1469    }
1470
1471    #[instrument(
1472        level = "info",
1473        skip_all,
1474        fields(uuid = ?eventid)
1475    )]
1476    pub async fn handle_oauth2_openid_publickey(
1477        &self,
1478        client_id: String,
1479        eventid: Uuid,
1480    ) -> Result<JwkKeySet, OperationError> {
1481        let idms_prox_read = self.idms.proxy_read().await?;
1482        idms_prox_read.oauth2_openid_publickey(&client_id)
1483    }
1484
1485    #[instrument(
1486        level = "info",
1487        skip_all,
1488        fields(uuid = ?eventid)
1489    )]
1490    pub async fn handle_list_applinks(
1491        &self,
1492        client_auth_info: ClientAuthInfo,
1493        eventid: Uuid,
1494    ) -> Result<Vec<AppLink>, OperationError> {
1495        let ct = duration_from_epoch_now();
1496        let mut idms_prox_read = self.idms.proxy_read().await?;
1497        let ident = idms_prox_read
1498            .validate_client_auth_info_to_ident(client_auth_info, ct)
1499            .map_err(|e| {
1500                error!("Invalid identity: {:?}", e);
1501                e
1502            })?;
1503
1504        // Nice and easy!
1505        idms_prox_read.list_applinks(&ident)
1506    }
1507
1508    #[instrument(
1509        level = "info",
1510        skip_all,
1511        fields(uuid = ?eventid)
1512    )]
1513    pub async fn handle_auth_valid(
1514        &self,
1515        client_auth_info: ClientAuthInfo,
1516        eventid: Uuid,
1517    ) -> Result<(), OperationError> {
1518        let ct = duration_from_epoch_now();
1519        let mut idms_prox_read = self.idms.proxy_read().await?;
1520
1521        idms_prox_read
1522            .validate_client_auth_info_to_ident(client_auth_info, ct)
1523            .map(|_| ())
1524            .map_err(|e| {
1525                error!("Invalid identity: {:?}", e);
1526                e
1527            })
1528    }
1529
1530    #[instrument(
1531        level = "info",
1532        skip_all,
1533        fields(uuid = ?eventid)
1534    )]
1535    /// Retrieve a public jwk
1536    pub async fn handle_public_jwk_get(
1537        &self,
1538        key_id: String,
1539        eventid: Uuid,
1540    ) -> Result<Jwk, OperationError> {
1541        let mut idms_prox_read = self.idms.proxy_read().await?;
1542
1543        idms_prox_read.jws_public_jwk(key_id.as_str())
1544    }
1545
1546    #[instrument(
1547        level = "info",
1548        skip_all,
1549        fields(uuid = ?eventid)
1550    )]
1551    pub async fn handle_ldaprequest(
1552        &self,
1553        eventid: Uuid,
1554        protomsg: LdapMsg,
1555        uat: Option<LdapBoundToken>,
1556        ip_addr: IpAddr,
1557    ) -> Option<LdapResponseState> {
1558        let res = match ServerOps::try_from(protomsg) {
1559            Ok(server_op) => self
1560                .ldap
1561                .do_op(&self.idms, server_op, uat, ip_addr, eventid)
1562                .await
1563                .unwrap_or_else(|e| {
1564                    error!("do_op failed -> {:?}", e);
1565                    LdapResponseState::Disconnect(DisconnectionNotice::gen(
1566                        LdapResultCode::Other,
1567                        format!("Internal Server Error {:?}", &eventid).as_str(),
1568                    ))
1569                }),
1570            Err(_) => LdapResponseState::Disconnect(DisconnectionNotice::gen(
1571                LdapResultCode::ProtocolError,
1572                format!("Invalid Request {:?}", &eventid).as_str(),
1573            )),
1574        };
1575        Some(res)
1576    }
1577
1578    pub fn domain_info_read(&self) -> DomainInfoRead {
1579        self.idms.domain_read()
1580    }
1581}