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), ct),
1007            IdentifyUserRequest::DisplayCode => idms_prox_read.handle_identify_user_display_code(
1008                &IdentifyUserDisplayCodeEvent::new(target, ident),
1009                ct,
1010            ),
1011            IdentifyUserRequest::SubmitCode { other_totp } => idms_prox_read
1012                .handle_identify_user_submit_code(
1013                    &IdentifyUserSubmitCodeEvent::new(target, ident, other_totp),
1014                    ct,
1015                ),
1016        }
1017    }
1018
1019    #[instrument(
1020        level = "info",
1021        skip_all,
1022        fields(uuid = ?eventid)
1023    )]
1024    pub async fn handle_idmaccountunixauth(
1025        &self,
1026        client_auth_info: ClientAuthInfo,
1027        uuid_or_name: String,
1028        cred: String,
1029        eventid: Uuid,
1030    ) -> Result<Option<UnixUserToken>, OperationError> {
1031        let ct = duration_from_epoch_now();
1032        let mut idm_auth = self.idms.auth().await?;
1033        // resolve the id
1034        let ident = idm_auth
1035            .validate_client_auth_info_to_ident(client_auth_info, ct)
1036            .map_err(|e| {
1037                error!(err = ?e, "Invalid identity");
1038                e
1039            })?;
1040
1041        let target_uuid = idm_auth
1042            .qs_read
1043            .name_to_uuid(uuid_or_name.as_str())
1044            .map_err(|e| {
1045                admin_info!(err = ?e, "Error resolving as gidnumber continuing");
1046                e
1047            })?;
1048        // Make an event from the request
1049        let uuae = match UnixUserAuthEvent::from_parts(ident, target_uuid, cred) {
1050            Ok(s) => s,
1051            Err(e) => {
1052                error!(err = ?e, "Failed to begin unix auth");
1053                return Err(e);
1054            }
1055        };
1056
1057        security_info!(event = ?uuae, "Begin unix auth event");
1058
1059        let res = idm_auth
1060            .auth_unix(&uuae, ct)
1061            .await
1062            .and_then(|r| idm_auth.commit().map(|_| r));
1063
1064        security_info!(?res, "Sending result");
1065
1066        res
1067    }
1068
1069    #[instrument(
1070        level = "info",
1071        skip_all,
1072        fields(uuid = ?eventid)
1073    )]
1074    pub async fn handle_idmcredentialstatus(
1075        &self,
1076        client_auth_info: ClientAuthInfo,
1077        uuid_or_name: String,
1078        eventid: Uuid,
1079    ) -> Result<CredentialStatus, OperationError> {
1080        let ct = duration_from_epoch_now();
1081        let mut idms_prox_read = self.idms.proxy_read().await?;
1082
1083        let ident = idms_prox_read
1084            .validate_client_auth_info_to_ident(client_auth_info, ct)
1085            .map_err(|e| {
1086                error!(err = ?e, "Invalid identity");
1087                e
1088            })?;
1089        let target_uuid = idms_prox_read
1090            .qs_read
1091            .name_to_uuid(uuid_or_name.as_str())
1092            .map_err(|e| {
1093                error!(err = ?e, "Error resolving id to target");
1094                e
1095            })?;
1096
1097        // Make an event from the request
1098        let cse = match CredentialStatusEvent::from_parts(
1099            // &idms_prox_read.qs_read,
1100            ident,
1101            target_uuid,
1102        ) {
1103            Ok(s) => s,
1104            Err(e) => {
1105                error!(err = ?e, "Failed to begin credential status read");
1106                return Err(e);
1107            }
1108        };
1109
1110        trace!(?cse, "Begin event");
1111
1112        idms_prox_read.get_credentialstatus(&cse)
1113    }
1114
1115    #[instrument(
1116        level = "info",
1117        skip_all,
1118        fields(uuid = ?eventid)
1119    )]
1120    pub async fn handle_idmcredentialupdatestatus(
1121        &self,
1122        session_token: CUSessionToken,
1123        eventid: Uuid,
1124    ) -> Result<CUStatus, OperationError> {
1125        let session_token = JweCompact::from_str(&session_token.token)
1126            .map(|token_enc| CredentialUpdateSessionToken { token_enc })
1127            .map_err(|err| {
1128                error!(?err, "malformed token");
1129                OperationError::InvalidRequestState
1130            })?;
1131
1132        // Don't proceed unless the token parses
1133        let ct = duration_from_epoch_now();
1134        let idms_cred_update = self.idms.cred_update_transaction().await?;
1135
1136        idms_cred_update
1137            .credential_update_status(&session_token, ct)
1138            .map_err(|e| {
1139                error!(
1140                    err = ?e,
1141                    "Failed to begin credential_update_status",
1142                );
1143                e
1144            })
1145            .map(|sta| sta.into())
1146    }
1147
1148    #[instrument(
1149        level = "info",
1150        skip_all,
1151        fields(uuid = ?eventid)
1152    )]
1153    pub async fn handle_idmcredentialupdate(
1154        &self,
1155        session_token: CUSessionToken,
1156        scr: CURequest,
1157        eventid: Uuid,
1158    ) -> Result<CUStatus, OperationError> {
1159        let session_token = JweCompact::from_str(&session_token.token)
1160            .map(|token_enc| CredentialUpdateSessionToken { token_enc })
1161            .map_err(|err| {
1162                error!(?err, "Invalid Token - Must be a compact JWE");
1163                OperationError::InvalidRequestState
1164            })?;
1165
1166        let ct = duration_from_epoch_now();
1167        let idms_cred_update = self.idms.cred_update_transaction().await?;
1168
1169        debug!(?scr);
1170
1171        match scr {
1172            CURequest::PrimaryRemove => idms_cred_update
1173                .credential_primary_delete(&session_token, ct)
1174                .map_err(|e| {
1175                    error!(
1176                        err = ?e,
1177                        "Failed to begin credential_primary_delete",
1178                    );
1179                    e
1180                }),
1181            CURequest::Password(pw) => idms_cred_update
1182                .credential_primary_set_password(&session_token, ct, &pw)
1183                .map_err(|e| {
1184                    error!(
1185                        err = ?e,
1186                        "Failed to begin credential_primary_set_password",
1187                    );
1188                    e
1189                }),
1190            CURequest::CancelMFAReg => idms_cred_update
1191                .credential_update_cancel_mfareg(&session_token, ct)
1192                .map_err(|e| {
1193                    error!(
1194                        err = ?e,
1195                        "Failed to begin credential_update_cancel_mfareg",
1196                    );
1197                    e
1198                }),
1199            CURequest::TotpGenerate => idms_cred_update
1200                .credential_primary_init_totp(&session_token, ct)
1201                .map_err(|e| {
1202                    error!(
1203                        err = ?e,
1204                        "Failed to begin credential_primary_init_totp",
1205                    );
1206                    e
1207                }),
1208            CURequest::TotpVerify(totp_chal, label) => idms_cred_update
1209                .credential_primary_check_totp(&session_token, ct, totp_chal, &label)
1210                .map_err(|e| {
1211                    error!(
1212                        err = ?e,
1213                        "Failed to begin credential_primary_check_totp",
1214                    );
1215                    e
1216                }),
1217            CURequest::TotpAcceptSha1 => idms_cred_update
1218                .credential_primary_accept_sha1_totp(&session_token, ct)
1219                .map_err(|e| {
1220                    error!(
1221                        err = ?e,
1222                        "Failed to begin credential_primary_accept_sha1_totp",
1223                    );
1224                    e
1225                }),
1226            CURequest::TotpRemove(label) => idms_cred_update
1227                .credential_primary_remove_totp(&session_token, ct, &label)
1228                .map_err(|e| {
1229                    error!(
1230                        err = ?e,
1231                        "Failed to begin credential_primary_remove_totp",
1232                    );
1233                    e
1234                }),
1235            CURequest::BackupCodeGenerate => idms_cred_update
1236                .credential_primary_init_backup_codes(&session_token, ct)
1237                .map_err(|e| {
1238                    error!(
1239                        err = ?e,
1240                        "Failed to begin credential_primary_init_backup_codes",
1241                    );
1242                    e
1243                }),
1244            CURequest::BackupCodeRemove => idms_cred_update
1245                .credential_primary_remove_backup_codes(&session_token, ct)
1246                .map_err(|e| {
1247                    error!(
1248                        err = ?e,
1249                        "Failed to begin credential_primary_remove_backup_codes",
1250                    );
1251                    e
1252                }),
1253            CURequest::PasskeyInit => idms_cred_update
1254                .credential_passkey_init(&session_token, ct)
1255                .map_err(|e| {
1256                    error!(
1257                        err = ?e,
1258                        "Failed to begin credential_passkey_init",
1259                    );
1260                    e
1261                }),
1262            CURequest::PasskeyFinish(label, rpkc) => idms_cred_update
1263                .credential_passkey_finish(&session_token, ct, label, &rpkc)
1264                .map_err(|e| {
1265                    error!(
1266                        err = ?e,
1267                        "Failed to begin credential_passkey_finish",
1268                    );
1269                    e
1270                }),
1271            CURequest::PasskeyRemove(uuid) => idms_cred_update
1272                .credential_passkey_remove(&session_token, ct, uuid)
1273                .map_err(|e| {
1274                    error!(
1275                        err = ?e,
1276                        "Failed to begin credential_passkey_remove"
1277                    );
1278                    e
1279                }),
1280            CURequest::AttestedPasskeyInit => idms_cred_update
1281                .credential_attested_passkey_init(&session_token, ct)
1282                .map_err(|e| {
1283                    error!(
1284                        err = ?e,
1285                        "Failed to begin credential_attested_passkey_init"
1286                    );
1287                    e
1288                }),
1289            CURequest::AttestedPasskeyFinish(label, rpkc) => idms_cred_update
1290                .credential_attested_passkey_finish(&session_token, ct, label, &rpkc)
1291                .map_err(|e| {
1292                    error!(
1293                        err = ?e,
1294                        "Failed to begin credential_attested_passkey_finish"
1295                    );
1296                    e
1297                }),
1298            CURequest::AttestedPasskeyRemove(uuid) => idms_cred_update
1299                .credential_attested_passkey_remove(&session_token, ct, uuid)
1300                .map_err(|e| {
1301                    error!(
1302                        err = ?e,
1303                        "Failed to begin credential_attested_passkey_remove"
1304                    );
1305                    e
1306                }),
1307            CURequest::UnixPasswordRemove => idms_cred_update
1308                .credential_unix_delete(&session_token, ct)
1309                .inspect_err(|err| {
1310                    error!(?err, "Failed to begin credential_unix_delete");
1311                }),
1312            CURequest::UnixPassword(pw) => idms_cred_update
1313                .credential_unix_set_password(&session_token, ct, &pw)
1314                .inspect_err(|err| {
1315                    error!(?err, "Failed to begin credential_unix_set_password");
1316                }),
1317
1318            CURequest::SshPublicKey(label, pubkey) => idms_cred_update
1319                .credential_sshkey_add(&session_token, ct, label, pubkey)
1320                .inspect_err(|err| {
1321                    error!(?err, "Failed to begin credential_sshkey_remove");
1322                }),
1323
1324            CURequest::SshPublicKeyRemove(label) => idms_cred_update
1325                .credential_sshkey_remove(&session_token, ct, &label)
1326                .inspect_err(|err| {
1327                    error!(?err, "Failed to begin credential_sshkey_remove");
1328                }),
1329        }
1330        .map(|sta| sta.into())
1331    }
1332
1333    #[instrument(
1334        level = "info",
1335        skip_all,
1336        fields(uuid = ?eventid)
1337    )]
1338    pub async fn handle_oauth2_basic_secret_read(
1339        &self,
1340        client_auth_info: ClientAuthInfo,
1341        filter: Filter<FilterInvalid>,
1342        eventid: Uuid,
1343    ) -> Result<Option<String>, OperationError> {
1344        let ct = duration_from_epoch_now();
1345        let mut idms_prox_read = self.idms.proxy_read().await?;
1346        let ident = idms_prox_read
1347            .validate_client_auth_info_to_ident(client_auth_info, ct)
1348            .map_err(|e| {
1349                error!("Invalid identity: {:?}", e);
1350                e
1351            })?;
1352
1353        // Make an event from the request
1354        let srch = match SearchEvent::from_internal_message(
1355            ident,
1356            &filter,
1357            None,
1358            &mut idms_prox_read.qs_read,
1359        ) {
1360            Ok(s) => s,
1361            Err(e) => {
1362                error!("Failed to begin oauth2 basic secret read: {:?}", e);
1363                return Err(e);
1364            }
1365        };
1366
1367        trace!(?srch, "Begin event");
1368
1369        // We have to use search_ext to guarantee acs was applied.
1370        match idms_prox_read.qs_read.search_ext(&srch) {
1371            Ok(mut entries) => {
1372                let r = entries
1373                    .pop()
1374                    // From the entry, turn it into the value
1375                    .and_then(|entry| {
1376                        entry
1377                            .get_ava_single(Attribute::OAuth2RsBasicSecret)
1378                            .and_then(|v| v.get_secret_str().map(str::to_string))
1379                    });
1380                Ok(r)
1381            }
1382            Err(e) => Err(e),
1383        }
1384    }
1385
1386    #[instrument(
1387        level = "info",
1388        skip_all,
1389        fields(uuid = ?eventid)
1390    )]
1391    pub async fn handle_oauth2_authorise(
1392        &self,
1393        client_auth_info: ClientAuthInfo,
1394        auth_req: AuthorisationRequest,
1395        eventid: Uuid,
1396    ) -> Result<AuthoriseResponse, Oauth2Error> {
1397        let ct = duration_from_epoch_now();
1398        let mut idms_prox_read = self
1399            .idms
1400            .proxy_read()
1401            .await
1402            .map_err(Oauth2Error::ServerError)?;
1403        let ident = idms_prox_read
1404            .validate_client_auth_info_to_ident(client_auth_info, ct)
1405            .inspect_err(|e| {
1406                error!("Invalid identity: {:?}", e);
1407            })
1408            .ok();
1409
1410        // Now we can send to the idm server for authorisation checking.
1411        idms_prox_read.check_oauth2_authorisation(ident.as_ref(), &auth_req, ct)
1412    }
1413
1414    #[instrument(
1415        level = "info",
1416        skip_all,
1417        fields(uuid = ?eventid)
1418    )]
1419    pub async fn handle_oauth2_authorise_reject(
1420        &self,
1421        client_auth_info: ClientAuthInfo,
1422        consent_req: String,
1423        eventid: Uuid,
1424    ) -> Result<AuthoriseReject, OperationError> {
1425        let ct = duration_from_epoch_now();
1426        let mut idms_prox_read = self.idms.proxy_read().await?;
1427        let ident = idms_prox_read
1428            .validate_client_auth_info_to_ident(client_auth_info, ct)
1429            .map_err(|e| {
1430                error!("Invalid identity: {:?}", e);
1431                e
1432            })?;
1433
1434        idms_prox_read.check_oauth2_authorise_reject(&ident, &consent_req, ct)
1435    }
1436
1437    #[instrument(
1438        level = "info",
1439        skip_all,
1440        fields(uuid = ?eventid)
1441    )]
1442    pub async fn handle_oauth2_token_introspect(
1443        &self,
1444        client_auth_info: ClientAuthInfo,
1445        intr_req: AccessTokenIntrospectRequest,
1446        eventid: Uuid,
1447    ) -> Result<AccessTokenIntrospectResponse, Oauth2Error> {
1448        let ct = duration_from_epoch_now();
1449        let mut idms_prox_read = self
1450            .idms
1451            .proxy_read()
1452            .await
1453            .map_err(Oauth2Error::ServerError)?;
1454        // Now we can send to the idm server for introspection checking.
1455        idms_prox_read.check_oauth2_token_introspect(&client_auth_info, &intr_req, ct)
1456    }
1457
1458    #[instrument(
1459        level = "info",
1460        skip_all,
1461        fields(uuid = ?eventid)
1462    )]
1463    pub async fn handle_oauth2_openid_userinfo(
1464        &self,
1465        client_id: String,
1466        token: &JwsCompact,
1467        eventid: Uuid,
1468    ) -> Result<OidcToken, Oauth2Error> {
1469        let ct = duration_from_epoch_now();
1470        let mut idms_prox_read = self
1471            .idms
1472            .proxy_read()
1473            .await
1474            .map_err(Oauth2Error::ServerError)?;
1475        idms_prox_read.oauth2_openid_userinfo(&client_id, token, ct)
1476    }
1477
1478    #[instrument(
1479        level = "info",
1480        skip_all,
1481        fields(uuid = ?eventid)
1482    )]
1483    pub async fn handle_oauth2_openid_discovery(
1484        &self,
1485        client_id: String,
1486        eventid: Uuid,
1487    ) -> Result<OidcDiscoveryResponse, OperationError> {
1488        let idms_prox_read = self.idms.proxy_read().await?;
1489        idms_prox_read.oauth2_openid_discovery(&client_id)
1490    }
1491
1492    #[instrument(
1493        level = "info",
1494        skip_all,
1495        fields(uuid = ?eventid)
1496    )]
1497    pub async fn handle_oauth2_webfinger_discovery(
1498        &self,
1499        client_id: &str,
1500        resource_id: &str,
1501        eventid: Uuid,
1502    ) -> Result<OidcWebfingerResponse, OperationError> {
1503        let mut idms_prox_read = self.idms.proxy_read().await?;
1504        idms_prox_read.oauth2_openid_webfinger(client_id, resource_id)
1505    }
1506
1507    #[instrument(
1508        level = "info",
1509        skip_all,
1510        fields(uuid = ?eventid)
1511    )]
1512    pub async fn handle_oauth2_rfc8414_metadata(
1513        &self,
1514        client_id: String,
1515        eventid: Uuid,
1516    ) -> Result<Oauth2Rfc8414MetadataResponse, OperationError> {
1517        let idms_prox_read = self.idms.proxy_read().await?;
1518        idms_prox_read.oauth2_rfc8414_metadata(&client_id)
1519    }
1520
1521    #[instrument(
1522        level = "info",
1523        skip_all,
1524        fields(uuid = ?eventid)
1525    )]
1526    pub async fn handle_oauth2_openid_publickey(
1527        &self,
1528        client_id: String,
1529        eventid: Uuid,
1530    ) -> Result<JwkKeySet, OperationError> {
1531        let idms_prox_read = self.idms.proxy_read().await?;
1532        idms_prox_read.oauth2_openid_publickey(&client_id)
1533    }
1534
1535    #[instrument(
1536        level = "info",
1537        skip_all,
1538        fields(uuid = ?eventid)
1539    )]
1540    pub async fn handle_list_applinks(
1541        &self,
1542        client_auth_info: ClientAuthInfo,
1543        eventid: Uuid,
1544    ) -> Result<Vec<AppLink>, OperationError> {
1545        let ct = duration_from_epoch_now();
1546        let mut idms_prox_read = self.idms.proxy_read().await?;
1547        let ident = idms_prox_read
1548            .validate_client_auth_info_to_ident(client_auth_info, ct)
1549            .map_err(|e| {
1550                error!("Invalid identity: {:?}", e);
1551                e
1552            })?;
1553
1554        // Nice and easy!
1555        idms_prox_read.list_applinks(&ident)
1556    }
1557
1558    #[instrument(
1559        level = "info",
1560        skip_all,
1561        fields(uuid = ?eventid)
1562    )]
1563    pub async fn handle_auth_valid(
1564        &self,
1565        client_auth_info: ClientAuthInfo,
1566        eventid: Uuid,
1567    ) -> Result<(), OperationError> {
1568        let ct = duration_from_epoch_now();
1569        let mut idms_prox_read = self.idms.proxy_read().await?;
1570
1571        idms_prox_read
1572            .validate_client_auth_info_to_ident(client_auth_info, ct)
1573            .map(|_| ())
1574            .map_err(|e| {
1575                error!("Invalid identity: {:?}", e);
1576                e
1577            })
1578    }
1579
1580    #[instrument(
1581        level = "info",
1582        skip_all,
1583        fields(uuid = ?eventid)
1584    )]
1585    /// Retrieve a public jwk
1586    pub async fn handle_public_jwk_get(
1587        &self,
1588        key_id: String,
1589        eventid: Uuid,
1590    ) -> Result<Jwk, OperationError> {
1591        let mut idms_prox_read = self.idms.proxy_read().await?;
1592
1593        idms_prox_read.jws_public_jwk(key_id.as_str())
1594    }
1595
1596    #[instrument(
1597        level = "info",
1598        skip_all,
1599        fields(uuid = ?eventid)
1600    )]
1601    pub async fn handle_ldaprequest(
1602        &self,
1603        eventid: Uuid,
1604        protomsg: LdapMsg,
1605        uat: Option<LdapBoundToken>,
1606        ip_addr: IpAddr,
1607    ) -> Option<LdapResponseState> {
1608        let res = match ServerOps::try_from(protomsg) {
1609            Ok(server_op) => self
1610                .ldap
1611                .do_op(&self.idms, server_op, uat, ip_addr, eventid)
1612                .await
1613                .unwrap_or_else(|e| {
1614                    error!("do_op failed -> {:?}", e);
1615                    LdapResponseState::Disconnect(DisconnectionNotice::gen(
1616                        LdapResultCode::Other,
1617                        format!("Internal Server Error {:?}", &eventid).as_str(),
1618                    ))
1619                }),
1620            Err(_) => LdapResponseState::Disconnect(DisconnectionNotice::gen(
1621                LdapResultCode::ProtocolError,
1622                format!("Invalid Request {:?}", &eventid).as_str(),
1623            )),
1624        };
1625        Some(res)
1626    }
1627
1628    pub fn domain_info_read(&self) -> DomainInfoRead {
1629        self.idms.domain_read()
1630    }
1631}