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