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, BackupCodesView, CURequest, CUSessionToken, CUStatus, CredentialStatus,
9    IdentifyUserRequest, IdentifyUserResponse, ImageValue, OperationError, RadiusAuthToken,
10    SearchRequest, 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, ReadBackupCodeEvent,
36        UnixGroupTokenEvent, 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-{}.json", timestamp));
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    #[instrument(
368        level = "info",
369        name = "whoami_uat",
370        skip_all,
371        fields(uuid = ?eventid)
372    )]
373    pub async fn handle_whoami_uat(
374        &self,
375        client_auth_info: ClientAuthInfo,
376        eventid: Uuid,
377    ) -> Result<UserAuthToken, OperationError> {
378        let ct = duration_from_epoch_now();
379        let mut idms_prox_read = self.idms.proxy_read().await?;
380        // Make an event from the whoami request. This will process the event and
381        // generate a selfuuid search.
382        //
383        // This current handles the unauthenticated check, and will
384        // trigger the failure, but if we can manage to work out async
385        // then move this to core.rs, and don't allow Option<UAT> to get
386        // this far.
387        idms_prox_read
388            .validate_client_auth_info_to_uat(client_auth_info, ct)
389            .map_err(|e| {
390                error!(?e, "Invalid identity");
391                e
392            })
393    }
394
395    #[instrument(level = "debug", skip_all)]
396    /// pull an image so we can present it to the user
397    pub async fn handle_oauth2_rs_image_get_image(
398        &self,
399        client_auth_info: ClientAuthInfo,
400        rs: Filter<FilterInvalid>,
401    ) -> Result<Option<ImageValue>, OperationError> {
402        let mut idms_prox_read = self.idms.proxy_read().await?;
403        let ct = duration_from_epoch_now();
404
405        let ident = idms_prox_read
406            .validate_client_auth_info_to_ident(client_auth_info, ct)
407            .map_err(|e| {
408                error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_get_image");
409                e
410            })?;
411        let attrs = vec![Attribute::Image.to_string()];
412
413        let search = SearchEvent::from_internal_message(
414            ident,
415            &rs,
416            Some(attrs.as_slice()),
417            &mut idms_prox_read.qs_read,
418        )?;
419
420        let entries = idms_prox_read.qs_read.search(&search)?;
421        Ok(entries
422            .first()
423            .and_then(|entry| entry.get_ava_single_image(Attribute::Image)))
424    }
425
426    #[instrument(
427        level = "info",
428        skip_all,
429        fields(uuid = ?eventid)
430    )]
431    pub async fn handle_internalsearch(
432        &self,
433        client_auth_info: ClientAuthInfo,
434        filter: Filter<FilterInvalid>,
435        attrs: Option<Vec<String>>,
436        eventid: Uuid,
437    ) -> Result<Vec<ProtoEntry>, OperationError> {
438        let ct = duration_from_epoch_now();
439        let mut idms_prox_read = self.idms.proxy_read().await?;
440        let ident = idms_prox_read
441            .validate_client_auth_info_to_ident(client_auth_info, ct)
442            .map_err(|e| {
443                error!("Invalid identity: {:?}", e);
444                e
445            })?;
446        // Make an event from the request
447        let srch = match SearchEvent::from_internal_message(
448            ident,
449            &filter,
450            attrs.as_deref(),
451            &mut idms_prox_read.qs_read,
452        ) {
453            Ok(s) => s,
454            Err(e) => {
455                error!("Failed to begin internal api search: {:?}", e);
456                return Err(e);
457            }
458        };
459
460        trace!(?srch, "Begin event");
461
462        match idms_prox_read.qs_read.search_ext(&srch) {
463            Ok(entries) => SearchResult::new(&mut idms_prox_read.qs_read, &entries)
464                .map(|ok_sr| ok_sr.into_proto_array()),
465            Err(e) => Err(e),
466        }
467    }
468
469    #[instrument(
470        level = "info",
471        skip_all,
472        fields(uuid = ?eventid)
473    )]
474    pub async fn handle_search_refers(
475        &self,
476        client_auth_info: ClientAuthInfo,
477        filter: Filter<FilterInvalid>,
478        uuid_or_name: String,
479        attrs: Option<Vec<String>>,
480        eventid: Uuid,
481    ) -> Result<Vec<ProtoEntry>, OperationError> {
482        let ct = duration_from_epoch_now();
483        let mut idms_prox_read = self.idms.proxy_read().await?;
484        let ident = idms_prox_read
485            .validate_client_auth_info_to_ident(client_auth_info, ct)
486            .map_err(|e| {
487                error!("Invalid identity: {:?}", e);
488                e
489            })?;
490
491        let target_uuid = idms_prox_read
492            .qs_read
493            .name_to_uuid(uuid_or_name.as_str())
494            .inspect_err(|err| {
495                error!(?err, "Error resolving id to target");
496            })?;
497
498        // Update the filter with the target_uuid
499        let filter = Filter::join_parts_and(
500            filter,
501            filter_all!(f_eq(Attribute::Refers, PartialValue::Refer(target_uuid))),
502        );
503
504        // Make an event from the request
505        let srch = match SearchEvent::from_internal_message(
506            ident,
507            &filter,
508            attrs.as_deref(),
509            &mut idms_prox_read.qs_read,
510        ) {
511            Ok(s) => s,
512            Err(e) => {
513                error!("Failed to begin internal api search: {:?}", e);
514                return Err(e);
515            }
516        };
517
518        trace!(?srch, "Begin event");
519
520        match idms_prox_read.qs_read.search_ext(&srch) {
521            Ok(entries) => SearchResult::new(&mut idms_prox_read.qs_read, &entries)
522                .map(|ok_sr| ok_sr.into_proto_array()),
523            Err(e) => Err(e),
524        }
525    }
526
527    #[instrument(
528        level = "info",
529        skip_all,
530        fields(uuid = ?eventid)
531    )]
532    pub async fn handle_internalsearchrecycled(
533        &self,
534        client_auth_info: ClientAuthInfo,
535        filter: Filter<FilterInvalid>,
536        attrs: Option<Vec<String>>,
537        eventid: Uuid,
538    ) -> Result<Vec<ProtoEntry>, OperationError> {
539        let ct = duration_from_epoch_now();
540        let mut idms_prox_read = self.idms.proxy_read().await?;
541
542        let ident = idms_prox_read
543            .validate_client_auth_info_to_ident(client_auth_info, ct)
544            .map_err(|e| {
545                error!("Invalid identity: {:?}", e);
546                e
547            })?;
548        // Make an event from the request
549        let srch = match SearchEvent::from_internal_recycle_message(
550            ident,
551            &filter,
552            attrs.as_deref(),
553            &idms_prox_read.qs_read,
554        ) {
555            Ok(s) => s,
556            Err(e) => {
557                error!("Failed to begin recycled search: {:?}", e);
558                return Err(e);
559            }
560        };
561
562        trace!(?srch, "Begin event");
563
564        match idms_prox_read.qs_read.search_ext(&srch) {
565            Ok(entries) => SearchResult::new(&mut idms_prox_read.qs_read, &entries)
566                .map(|ok_sr| ok_sr.into_proto_array()),
567            Err(e) => Err(e),
568        }
569    }
570
571    #[instrument(
572        level = "info",
573        skip_all,
574        fields(uuid = ?eventid)
575    )]
576    pub async fn handle_internalradiusread(
577        &self,
578        client_auth_info: ClientAuthInfo,
579        uuid_or_name: String,
580        eventid: Uuid,
581    ) -> Result<Option<String>, OperationError> {
582        let ct = duration_from_epoch_now();
583        let mut idms_prox_read = self.idms.proxy_read().await?;
584        let ident = idms_prox_read
585            .validate_client_auth_info_to_ident(client_auth_info, ct)
586            .map_err(|e| {
587                error!("Invalid identity: {:?}", e);
588                e
589            })?;
590
591        let target_uuid = idms_prox_read
592            .qs_read
593            .name_to_uuid(uuid_or_name.as_str())
594            .inspect_err(|err| {
595                error!(?err, "Error resolving id to target");
596            })?;
597
598        // Make an event from the request
599        let srch = match SearchEvent::from_target_uuid_request(
600            ident,
601            target_uuid,
602            &idms_prox_read.qs_read,
603        ) {
604            Ok(s) => s,
605            Err(e) => {
606                error!("Failed to begin radius read: {:?}", e);
607                return Err(e);
608            }
609        };
610
611        trace!(?srch, "Begin event");
612
613        // We have to use search_ext to guarantee acs was applied.
614        match idms_prox_read.qs_read.search_ext(&srch) {
615            Ok(mut entries) => {
616                let r = entries
617                    .pop()
618                    // From the entry, turn it into the value
619                    .and_then(|entry| {
620                        entry
621                            .get_ava_single(Attribute::RadiusSecret)
622                            .and_then(|v| v.get_secret_str().map(str::to_string))
623                    });
624                Ok(r)
625            }
626            Err(e) => Err(e),
627        }
628    }
629
630    #[instrument(
631        level = "info",
632        skip_all,
633        fields(uuid = ?eventid)
634    )]
635    pub async fn handle_internalradiustokenread(
636        &self,
637        client_auth_info: ClientAuthInfo,
638        uuid_or_name: String,
639        eventid: Uuid,
640    ) -> Result<RadiusAuthToken, OperationError> {
641        let ct = duration_from_epoch_now();
642        let mut idms_prox_read = self.idms.proxy_read().await?;
643
644        let ident = idms_prox_read
645            .validate_client_auth_info_to_ident(client_auth_info, ct)
646            .map_err(|e| {
647                error!("Invalid identity: {:?}", e);
648                e
649            })?;
650
651        let target_uuid = idms_prox_read
652            .qs_read
653            .name_to_uuid(uuid_or_name.as_str())
654            .inspect_err(|err| {
655                error!(?err, "Error resolving id to target");
656            })?;
657
658        // Make an event from the request
659        let rate = match RadiusAuthTokenEvent::from_parts(
660            // &idms_prox_read.qs_read,
661            ident,
662            target_uuid,
663        ) {
664            Ok(s) => s,
665            Err(e) => {
666                error!("Failed to begin radius token read: {:?}", e);
667                return Err(e);
668            }
669        };
670
671        trace!(?rate, "Begin event");
672
673        idms_prox_read.get_radiusauthtoken(&rate, ct)
674    }
675
676    #[instrument(
677        level = "info",
678        skip_all,
679        fields(uuid = ?eventid)
680    )]
681    pub async fn handle_internalunixusertokenread(
682        &self,
683        client_auth_info: ClientAuthInfo,
684        uuid_or_name: String,
685        eventid: Uuid,
686    ) -> Result<UnixUserToken, OperationError> {
687        let ct = duration_from_epoch_now();
688        let mut idms_prox_read = self.idms.proxy_read().await?;
689
690        let ident = idms_prox_read
691            .validate_client_auth_info_to_ident(client_auth_info, ct)
692            .map_err(|e| {
693                error!("Invalid identity: {:?}", e);
694                e
695            })?;
696
697        let target_uuid = idms_prox_read
698            .qs_read
699            .name_to_uuid(uuid_or_name.as_str())
700            .map_err(|e| {
701                // sometimes it comes back as empty which is bad, it's safe to start with `<empty` here
702                // because a valid username/uuid can never start with that and we're only logging it
703                let uuid_or_name_val = match uuid_or_name.is_empty() {
704                    true => "<empty uuid_or_name>",
705                    false => &uuid_or_name,
706                };
707                admin_info!(
708                    err = ?e,
709                    "Error resolving {} as gidnumber continuing ...",
710                    uuid_or_name_val
711                );
712                e
713            })?;
714
715        // Make an event from the request
716        let rate = match UnixUserTokenEvent::from_parts(ident, target_uuid) {
717            Ok(s) => s,
718            Err(e) => {
719                error!("Failed to begin unix token read: {:?}", e);
720                return Err(e);
721            }
722        };
723
724        trace!(?rate, "Begin event");
725
726        idms_prox_read.get_unixusertoken(&rate, ct)
727    }
728
729    #[instrument(
730        level = "info",
731        skip_all,
732        fields(uuid = ?eventid)
733    )]
734    pub async fn handle_internalunixgrouptokenread(
735        &self,
736        client_auth_info: ClientAuthInfo,
737        uuid_or_name: String,
738        eventid: Uuid,
739    ) -> Result<UnixGroupToken, OperationError> {
740        let ct = duration_from_epoch_now();
741        let mut idms_prox_read = self.idms.proxy_read().await?;
742        let ident = idms_prox_read
743            .validate_client_auth_info_to_ident(client_auth_info, ct)
744            .map_err(|e| {
745                error!("Invalid identity: {:?}", e);
746                e
747            })?;
748
749        let target_uuid = idms_prox_read
750            .qs_read
751            .name_to_uuid(uuid_or_name.as_str())
752            .map_err(|e| {
753                admin_info!(err = ?e, "Error resolving as gidnumber continuing");
754                e
755            })?;
756
757        // Make an event from the request
758        let rate = match UnixGroupTokenEvent::from_parts(
759            // &idms_prox_read.qs_read,
760            ident,
761            target_uuid,
762        ) {
763            Ok(s) => s,
764            Err(e) => {
765                error!("Failed to begin unix group token read: {:?}", e);
766                return Err(e);
767            }
768        };
769
770        trace!(?rate, "Begin event");
771
772        idms_prox_read.get_unixgrouptoken(&rate)
773    }
774
775    #[instrument(
776        level = "info",
777        skip_all,
778        fields(uuid = ?eventid)
779    )]
780    pub async fn handle_internalsshkeyread(
781        &self,
782        client_auth_info: ClientAuthInfo,
783        uuid_or_name: String,
784        eventid: Uuid,
785    ) -> Result<Vec<String>, OperationError> {
786        let ct = duration_from_epoch_now();
787        let mut idms_prox_read = self.idms.proxy_read().await?;
788        let ident = idms_prox_read
789            .validate_client_auth_info_to_ident(client_auth_info, ct)
790            .map_err(|e| {
791                error!("Invalid identity: {:?}", e);
792                e
793            })?;
794        let target_uuid = idms_prox_read
795            .qs_read
796            .name_to_uuid(uuid_or_name.as_str())
797            .inspect_err(|err| {
798                error!(?err, "Error resolving id to target");
799            })?;
800
801        // Make an event from the request
802        let srch = match SearchEvent::from_target_uuid_request(
803            ident,
804            target_uuid,
805            &idms_prox_read.qs_read,
806        ) {
807            Ok(s) => s,
808            Err(e) => {
809                error!("Failed to begin ssh key read: {:?}", e);
810                return Err(e);
811            }
812        };
813
814        trace!(?srch, "Begin event");
815
816        match idms_prox_read.qs_read.search_ext(&srch) {
817            Ok(mut entries) => {
818                let r = entries
819                    .pop()
820                    // get the first entry
821                    .and_then(|e| {
822                        // From the entry, turn it into the value
823                        e.get_ava_iter_sshpubkeys(Attribute::SshPublicKey)
824                            .map(|i| i.collect())
825                    })
826                    .unwrap_or_else(|| {
827                        // No matching entry? Return none.
828                        Vec::new()
829                    });
830                Ok(r)
831            }
832            Err(e) => Err(e),
833        }
834    }
835
836    #[instrument(
837        level = "info",
838        skip_all,
839        fields(uuid = ?eventid)
840    )]
841    pub async fn handle_internalsshkeytagread(
842        &self,
843        client_auth_info: ClientAuthInfo,
844        uuid_or_name: String,
845        tag: String,
846        eventid: Uuid,
847    ) -> Result<Option<String>, OperationError> {
848        let ct = duration_from_epoch_now();
849        let mut idms_prox_read = self.idms.proxy_read().await?;
850        let ident = idms_prox_read
851            .validate_client_auth_info_to_ident(client_auth_info, ct)
852            .map_err(|e| {
853                error!("Invalid identity: {:?}", e);
854                e
855            })?;
856        let target_uuid = idms_prox_read
857            .qs_read
858            .name_to_uuid(uuid_or_name.as_str())
859            .inspect_err(|err| {
860                admin_info!(?err, "Error resolving id to target");
861            })?;
862
863        // Make an event from the request
864        let srch = match SearchEvent::from_target_uuid_request(
865            ident,
866            target_uuid,
867            &idms_prox_read.qs_read,
868        ) {
869            Ok(s) => s,
870            Err(e) => {
871                error!("Failed to begin sshkey tag read: {:?}", e);
872                return Err(e);
873            }
874        };
875
876        trace!(?srch, "Begin event");
877
878        match idms_prox_read.qs_read.search_ext(&srch) {
879            Ok(mut entries) => {
880                let r = entries
881                    .pop()
882                    // get the first entry
883                    .map(|e| {
884                        // From the entry, turn it into the value
885                        e.get_ava_set(Attribute::SshPublicKey).and_then(|vs| {
886                            // Get the one tagged value
887                            vs.get_ssh_tag(&tag).map(|pk| pk.to_string())
888                        })
889                    })
890                    .unwrap_or_else(|| {
891                        // No matching entry? Return none.
892                        None
893                    });
894                Ok(r)
895            }
896            Err(e) => Err(e),
897        }
898    }
899
900    #[instrument(
901        level = "info",
902        skip_all,
903        fields(uuid = ?eventid)
904    )]
905    pub async fn handle_service_account_api_token_get(
906        &self,
907        client_auth_info: ClientAuthInfo,
908        uuid_or_name: String,
909        eventid: Uuid,
910    ) -> Result<Vec<ApiToken>, OperationError> {
911        let ct = duration_from_epoch_now();
912        let mut idms_prox_read = self.idms.proxy_read().await?;
913        let ident = idms_prox_read
914            .validate_client_auth_info_to_ident(client_auth_info, ct)
915            .map_err(|e| {
916                error!("Invalid identity: {:?}", e);
917                e
918            })?;
919        let target = idms_prox_read
920            .qs_read
921            .name_to_uuid(uuid_or_name.as_str())
922            .inspect_err(|err| {
923                error!(?err, "Error resolving id to target");
924            })?;
925
926        let lte = ListApiTokenEvent { ident, target };
927
928        idms_prox_read.service_account_list_api_token(&lte)
929    }
930
931    #[instrument(
932        level = "info",
933        skip_all,
934        fields(uuid = ?eventid)
935    )]
936    pub async fn handle_account_user_auth_token_get(
937        &self,
938        client_auth_info: ClientAuthInfo,
939        uuid_or_name: String,
940        eventid: Uuid,
941    ) -> Result<Vec<UatStatus>, OperationError> {
942        let ct = duration_from_epoch_now();
943        let mut idms_prox_read = self.idms.proxy_read().await?;
944        let ident = idms_prox_read
945            .validate_client_auth_info_to_ident(client_auth_info, ct)
946            .map_err(|e| {
947                error!("Invalid identity: {:?}", e);
948                e
949            })?;
950        let target = idms_prox_read
951            .qs_read
952            .name_to_uuid(uuid_or_name.as_str())
953            .inspect_err(|err| {
954                error!(?err, "Error resolving id to target");
955            })?;
956
957        let lte = ListUserAuthTokenEvent { ident, target };
958
959        idms_prox_read.account_list_user_auth_tokens(&lte)
960    }
961
962    #[instrument(
963        level = "info",
964        skip_all,
965        fields(uuid = ?eventid)
966    )]
967    pub async fn handle_user_identity_verification(
968        &self,
969        client_auth_info: ClientAuthInfo,
970        eventid: Uuid,
971        user_request: IdentifyUserRequest,
972        other_id: String,
973    ) -> Result<IdentifyUserResponse, OperationError> {
974        trace!("{:?}", &user_request);
975        let ct = duration_from_epoch_now();
976        let mut idms_prox_read = self.idms.proxy_read().await?;
977        let ident = idms_prox_read
978            .validate_client_auth_info_to_ident(client_auth_info, ct)
979            .map_err(|e| {
980                error!("Invalid identity: {:?}", e);
981                e
982            })?;
983        let target = idms_prox_read
984            .qs_read
985            .name_to_uuid(&other_id)
986            .map_err(|e| {
987                error!("No user found with the provided ID: {:?}", e);
988                e
989            })?;
990        match user_request {
991            IdentifyUserRequest::Start => idms_prox_read
992                .handle_identify_user_start(&IdentifyUserStartEvent::new(target, ident)),
993            IdentifyUserRequest::DisplayCode => idms_prox_read.handle_identify_user_display_code(
994                &IdentifyUserDisplayCodeEvent::new(target, ident),
995            ),
996            IdentifyUserRequest::SubmitCode { other_totp } => idms_prox_read
997                .handle_identify_user_submit_code(&IdentifyUserSubmitCodeEvent::new(
998                    target, ident, other_totp,
999                )),
1000        }
1001    }
1002
1003    #[instrument(
1004        level = "info",
1005        skip_all,
1006        fields(uuid = ?eventid)
1007    )]
1008    pub async fn handle_idmaccountunixauth(
1009        &self,
1010        client_auth_info: ClientAuthInfo,
1011        uuid_or_name: String,
1012        cred: String,
1013        eventid: Uuid,
1014    ) -> Result<Option<UnixUserToken>, OperationError> {
1015        let ct = duration_from_epoch_now();
1016        let mut idm_auth = self.idms.auth().await?;
1017        // resolve the id
1018        let ident = idm_auth
1019            .validate_client_auth_info_to_ident(client_auth_info, ct)
1020            .map_err(|e| {
1021                error!(err = ?e, "Invalid identity");
1022                e
1023            })?;
1024
1025        let target_uuid = idm_auth
1026            .qs_read
1027            .name_to_uuid(uuid_or_name.as_str())
1028            .map_err(|e| {
1029                admin_info!(err = ?e, "Error resolving as gidnumber continuing");
1030                e
1031            })?;
1032        // Make an event from the request
1033        let uuae = match UnixUserAuthEvent::from_parts(ident, target_uuid, cred) {
1034            Ok(s) => s,
1035            Err(e) => {
1036                error!(err = ?e, "Failed to begin unix auth");
1037                return Err(e);
1038            }
1039        };
1040
1041        security_info!(event = ?uuae, "Begin unix auth event");
1042
1043        let res = idm_auth
1044            .auth_unix(&uuae, ct)
1045            .await
1046            .and_then(|r| idm_auth.commit().map(|_| r));
1047
1048        security_info!(?res, "Sending result");
1049
1050        res
1051    }
1052
1053    #[instrument(
1054        level = "info",
1055        skip_all,
1056        fields(uuid = ?eventid)
1057    )]
1058    pub async fn handle_idmcredentialstatus(
1059        &self,
1060        client_auth_info: ClientAuthInfo,
1061        uuid_or_name: String,
1062        eventid: Uuid,
1063    ) -> Result<CredentialStatus, OperationError> {
1064        let ct = duration_from_epoch_now();
1065        let mut idms_prox_read = self.idms.proxy_read().await?;
1066
1067        let ident = idms_prox_read
1068            .validate_client_auth_info_to_ident(client_auth_info, ct)
1069            .map_err(|e| {
1070                error!(err = ?e, "Invalid identity");
1071                e
1072            })?;
1073        let target_uuid = idms_prox_read
1074            .qs_read
1075            .name_to_uuid(uuid_or_name.as_str())
1076            .map_err(|e| {
1077                error!(err = ?e, "Error resolving id to target");
1078                e
1079            })?;
1080
1081        // Make an event from the request
1082        let cse = match CredentialStatusEvent::from_parts(
1083            // &idms_prox_read.qs_read,
1084            ident,
1085            target_uuid,
1086        ) {
1087            Ok(s) => s,
1088            Err(e) => {
1089                error!(err = ?e, "Failed to begin credential status read");
1090                return Err(e);
1091            }
1092        };
1093
1094        trace!(?cse, "Begin event");
1095
1096        idms_prox_read.get_credentialstatus(&cse)
1097    }
1098
1099    #[instrument(
1100        level = "info",
1101        skip_all,
1102        fields(uuid = ?eventid)
1103    )]
1104    pub async fn handle_idmbackupcodeview(
1105        &self,
1106        client_auth_info: ClientAuthInfo,
1107        uuid_or_name: String,
1108        eventid: Uuid,
1109    ) -> Result<BackupCodesView, OperationError> {
1110        let ct = duration_from_epoch_now();
1111        let mut idms_prox_read = self.idms.proxy_read().await?;
1112
1113        let ident = idms_prox_read
1114            .validate_client_auth_info_to_ident(client_auth_info, ct)
1115            .map_err(|e| {
1116                error!("Invalid identity: {:?}", e);
1117                e
1118            })?;
1119        let target_uuid = idms_prox_read
1120            .qs_read
1121            .name_to_uuid(uuid_or_name.as_str())
1122            .inspect_err(|err| {
1123                error!(?err, "Error resolving id to target");
1124            })?;
1125
1126        // Make an event from the request
1127        let rbce = match ReadBackupCodeEvent::from_parts(
1128            // &idms_prox_read.qs_read,
1129            ident,
1130            target_uuid,
1131        ) {
1132            Ok(s) => s,
1133            Err(e) => {
1134                error!("Failed to begin backup code read: {:?}", e);
1135                return Err(e);
1136            }
1137        };
1138
1139        trace!(?rbce, "Begin event");
1140
1141        idms_prox_read.get_backup_codes(&rbce)
1142    }
1143
1144    #[instrument(
1145        level = "info",
1146        skip_all,
1147        fields(uuid = ?eventid)
1148    )]
1149    pub async fn handle_idmcredentialupdatestatus(
1150        &self,
1151        session_token: CUSessionToken,
1152        eventid: Uuid,
1153    ) -> Result<CUStatus, OperationError> {
1154        let session_token = JweCompact::from_str(&session_token.token)
1155            .map(|token_enc| CredentialUpdateSessionToken { token_enc })
1156            .map_err(|err| {
1157                error!(?err, "malformed token");
1158                OperationError::InvalidRequestState
1159            })?;
1160
1161        // Don't proceed unless the token parses
1162        let ct = duration_from_epoch_now();
1163        let idms_cred_update = self.idms.cred_update_transaction().await?;
1164
1165        idms_cred_update
1166            .credential_update_status(&session_token, ct)
1167            .map_err(|e| {
1168                error!(
1169                    err = ?e,
1170                    "Failed to begin credential_update_status",
1171                );
1172                e
1173            })
1174            .map(|sta| sta.into())
1175    }
1176
1177    #[instrument(
1178        level = "info",
1179        skip_all,
1180        fields(uuid = ?eventid)
1181    )]
1182    pub async fn handle_idmcredentialupdate(
1183        &self,
1184        session_token: CUSessionToken,
1185        scr: CURequest,
1186        eventid: Uuid,
1187    ) -> Result<CUStatus, OperationError> {
1188        let session_token = JweCompact::from_str(&session_token.token)
1189            .map(|token_enc| CredentialUpdateSessionToken { token_enc })
1190            .map_err(|err| {
1191                error!(?err, "Invalid Token - Must be a compact JWE");
1192                OperationError::InvalidRequestState
1193            })?;
1194
1195        let ct = duration_from_epoch_now();
1196        let idms_cred_update = self.idms.cred_update_transaction().await?;
1197
1198        debug!(?scr);
1199
1200        match scr {
1201            CURequest::PrimaryRemove => idms_cred_update
1202                .credential_primary_delete(&session_token, ct)
1203                .map_err(|e| {
1204                    error!(
1205                        err = ?e,
1206                        "Failed to begin credential_primary_delete",
1207                    );
1208                    e
1209                }),
1210            CURequest::Password(pw) => idms_cred_update
1211                .credential_primary_set_password(&session_token, ct, &pw)
1212                .map_err(|e| {
1213                    error!(
1214                        err = ?e,
1215                        "Failed to begin credential_primary_set_password",
1216                    );
1217                    e
1218                }),
1219            CURequest::CancelMFAReg => idms_cred_update
1220                .credential_update_cancel_mfareg(&session_token, ct)
1221                .map_err(|e| {
1222                    error!(
1223                        err = ?e,
1224                        "Failed to begin credential_update_cancel_mfareg",
1225                    );
1226                    e
1227                }),
1228            CURequest::TotpGenerate => idms_cred_update
1229                .credential_primary_init_totp(&session_token, ct)
1230                .map_err(|e| {
1231                    error!(
1232                        err = ?e,
1233                        "Failed to begin credential_primary_init_totp",
1234                    );
1235                    e
1236                }),
1237            CURequest::TotpVerify(totp_chal, label) => idms_cred_update
1238                .credential_primary_check_totp(&session_token, ct, totp_chal, &label)
1239                .map_err(|e| {
1240                    error!(
1241                        err = ?e,
1242                        "Failed to begin credential_primary_check_totp",
1243                    );
1244                    e
1245                }),
1246            CURequest::TotpAcceptSha1 => idms_cred_update
1247                .credential_primary_accept_sha1_totp(&session_token, ct)
1248                .map_err(|e| {
1249                    error!(
1250                        err = ?e,
1251                        "Failed to begin credential_primary_accept_sha1_totp",
1252                    );
1253                    e
1254                }),
1255            CURequest::TotpRemove(label) => idms_cred_update
1256                .credential_primary_remove_totp(&session_token, ct, &label)
1257                .map_err(|e| {
1258                    error!(
1259                        err = ?e,
1260                        "Failed to begin credential_primary_remove_totp",
1261                    );
1262                    e
1263                }),
1264            CURequest::BackupCodeGenerate => idms_cred_update
1265                .credential_primary_init_backup_codes(&session_token, ct)
1266                .map_err(|e| {
1267                    error!(
1268                        err = ?e,
1269                        "Failed to begin credential_primary_init_backup_codes",
1270                    );
1271                    e
1272                }),
1273            CURequest::BackupCodeRemove => idms_cred_update
1274                .credential_primary_remove_backup_codes(&session_token, ct)
1275                .map_err(|e| {
1276                    error!(
1277                        err = ?e,
1278                        "Failed to begin credential_primary_remove_backup_codes",
1279                    );
1280                    e
1281                }),
1282            CURequest::PasskeyInit => idms_cred_update
1283                .credential_passkey_init(&session_token, ct)
1284                .map_err(|e| {
1285                    error!(
1286                        err = ?e,
1287                        "Failed to begin credential_passkey_init",
1288                    );
1289                    e
1290                }),
1291            CURequest::PasskeyFinish(label, rpkc) => idms_cred_update
1292                .credential_passkey_finish(&session_token, ct, label, &rpkc)
1293                .map_err(|e| {
1294                    error!(
1295                        err = ?e,
1296                        "Failed to begin credential_passkey_finish",
1297                    );
1298                    e
1299                }),
1300            CURequest::PasskeyRemove(uuid) => idms_cred_update
1301                .credential_passkey_remove(&session_token, ct, uuid)
1302                .map_err(|e| {
1303                    error!(
1304                        err = ?e,
1305                        "Failed to begin credential_passkey_remove"
1306                    );
1307                    e
1308                }),
1309            CURequest::AttestedPasskeyInit => idms_cred_update
1310                .credential_attested_passkey_init(&session_token, ct)
1311                .map_err(|e| {
1312                    error!(
1313                        err = ?e,
1314                        "Failed to begin credential_attested_passkey_init"
1315                    );
1316                    e
1317                }),
1318            CURequest::AttestedPasskeyFinish(label, rpkc) => idms_cred_update
1319                .credential_attested_passkey_finish(&session_token, ct, label, &rpkc)
1320                .map_err(|e| {
1321                    error!(
1322                        err = ?e,
1323                        "Failed to begin credential_attested_passkey_finish"
1324                    );
1325                    e
1326                }),
1327            CURequest::AttestedPasskeyRemove(uuid) => idms_cred_update
1328                .credential_attested_passkey_remove(&session_token, ct, uuid)
1329                .map_err(|e| {
1330                    error!(
1331                        err = ?e,
1332                        "Failed to begin credential_attested_passkey_remove"
1333                    );
1334                    e
1335                }),
1336            CURequest::UnixPasswordRemove => idms_cred_update
1337                .credential_unix_delete(&session_token, ct)
1338                .inspect_err(|err| {
1339                    error!(?err, "Failed to begin credential_unix_delete");
1340                }),
1341            CURequest::UnixPassword(pw) => idms_cred_update
1342                .credential_unix_set_password(&session_token, ct, &pw)
1343                .inspect_err(|err| {
1344                    error!(?err, "Failed to begin credential_unix_set_password");
1345                }),
1346
1347            CURequest::SshPublicKey(label, pubkey) => idms_cred_update
1348                .credential_sshkey_add(&session_token, ct, label, pubkey)
1349                .inspect_err(|err| {
1350                    error!(?err, "Failed to begin credential_sshkey_remove");
1351                }),
1352
1353            CURequest::SshPublicKeyRemove(label) => idms_cred_update
1354                .credential_sshkey_remove(&session_token, ct, &label)
1355                .inspect_err(|err| {
1356                    error!(?err, "Failed to begin credential_sshkey_remove");
1357                }),
1358        }
1359        .map(|sta| sta.into())
1360    }
1361
1362    #[instrument(
1363        level = "info",
1364        skip_all,
1365        fields(uuid = ?eventid)
1366    )]
1367    pub async fn handle_oauth2_basic_secret_read(
1368        &self,
1369        client_auth_info: ClientAuthInfo,
1370        filter: Filter<FilterInvalid>,
1371        eventid: Uuid,
1372    ) -> Result<Option<String>, OperationError> {
1373        let ct = duration_from_epoch_now();
1374        let mut idms_prox_read = self.idms.proxy_read().await?;
1375        let ident = idms_prox_read
1376            .validate_client_auth_info_to_ident(client_auth_info, ct)
1377            .map_err(|e| {
1378                error!("Invalid identity: {:?}", e);
1379                e
1380            })?;
1381
1382        // Make an event from the request
1383        let srch = match SearchEvent::from_internal_message(
1384            ident,
1385            &filter,
1386            None,
1387            &mut idms_prox_read.qs_read,
1388        ) {
1389            Ok(s) => s,
1390            Err(e) => {
1391                error!("Failed to begin oauth2 basic secret read: {:?}", e);
1392                return Err(e);
1393            }
1394        };
1395
1396        trace!(?srch, "Begin event");
1397
1398        // We have to use search_ext to guarantee acs was applied.
1399        match idms_prox_read.qs_read.search_ext(&srch) {
1400            Ok(mut entries) => {
1401                let r = entries
1402                    .pop()
1403                    // From the entry, turn it into the value
1404                    .and_then(|entry| {
1405                        entry
1406                            .get_ava_single(Attribute::OAuth2RsBasicSecret)
1407                            .and_then(|v| v.get_secret_str().map(str::to_string))
1408                    });
1409                Ok(r)
1410            }
1411            Err(e) => Err(e),
1412        }
1413    }
1414
1415    #[instrument(
1416        level = "info",
1417        skip_all,
1418        fields(uuid = ?eventid)
1419    )]
1420    pub async fn handle_oauth2_authorise(
1421        &self,
1422        client_auth_info: ClientAuthInfo,
1423        auth_req: AuthorisationRequest,
1424        eventid: Uuid,
1425    ) -> Result<AuthoriseResponse, Oauth2Error> {
1426        let ct = duration_from_epoch_now();
1427        let mut idms_prox_read = self
1428            .idms
1429            .proxy_read()
1430            .await
1431            .map_err(Oauth2Error::ServerError)?;
1432        let ident = idms_prox_read
1433            .validate_client_auth_info_to_ident(client_auth_info, ct)
1434            .inspect_err(|e| {
1435                error!("Invalid identity: {:?}", e);
1436            })
1437            .ok();
1438
1439        // Now we can send to the idm server for authorisation checking.
1440        idms_prox_read.check_oauth2_authorisation(ident.as_ref(), &auth_req, ct)
1441    }
1442
1443    #[instrument(
1444        level = "info",
1445        skip_all,
1446        fields(uuid = ?eventid)
1447    )]
1448    pub async fn handle_oauth2_authorise_reject(
1449        &self,
1450        client_auth_info: ClientAuthInfo,
1451        consent_req: String,
1452        eventid: Uuid,
1453    ) -> Result<AuthoriseReject, OperationError> {
1454        let ct = duration_from_epoch_now();
1455        let mut idms_prox_read = self.idms.proxy_read().await?;
1456        let ident = idms_prox_read
1457            .validate_client_auth_info_to_ident(client_auth_info, ct)
1458            .map_err(|e| {
1459                error!("Invalid identity: {:?}", e);
1460                e
1461            })?;
1462
1463        idms_prox_read.check_oauth2_authorise_reject(&ident, &consent_req, ct)
1464    }
1465
1466    #[instrument(
1467        level = "info",
1468        skip_all,
1469        fields(uuid = ?eventid)
1470    )]
1471    pub async fn handle_oauth2_token_introspect(
1472        &self,
1473        client_auth_info: ClientAuthInfo,
1474        intr_req: AccessTokenIntrospectRequest,
1475        eventid: Uuid,
1476    ) -> Result<AccessTokenIntrospectResponse, Oauth2Error> {
1477        let ct = duration_from_epoch_now();
1478        let mut idms_prox_read = self
1479            .idms
1480            .proxy_read()
1481            .await
1482            .map_err(Oauth2Error::ServerError)?;
1483        // Now we can send to the idm server for introspection checking.
1484        idms_prox_read.check_oauth2_token_introspect(&client_auth_info, &intr_req, ct)
1485    }
1486
1487    #[instrument(
1488        level = "info",
1489        skip_all,
1490        fields(uuid = ?eventid)
1491    )]
1492    pub async fn handle_oauth2_openid_userinfo(
1493        &self,
1494        client_id: String,
1495        token: JwsCompact,
1496        eventid: Uuid,
1497    ) -> Result<OidcToken, Oauth2Error> {
1498        let ct = duration_from_epoch_now();
1499        let mut idms_prox_read = self
1500            .idms
1501            .proxy_read()
1502            .await
1503            .map_err(Oauth2Error::ServerError)?;
1504        idms_prox_read.oauth2_openid_userinfo(&client_id, token, ct)
1505    }
1506
1507    #[instrument(
1508        level = "info",
1509        skip_all,
1510        fields(uuid = ?eventid)
1511    )]
1512    pub async fn handle_oauth2_openid_discovery(
1513        &self,
1514        client_id: String,
1515        eventid: Uuid,
1516    ) -> Result<OidcDiscoveryResponse, OperationError> {
1517        let idms_prox_read = self.idms.proxy_read().await?;
1518        idms_prox_read.oauth2_openid_discovery(&client_id)
1519    }
1520
1521    #[instrument(
1522        level = "info",
1523        skip_all,
1524        fields(uuid = ?eventid)
1525    )]
1526    pub async fn handle_oauth2_webfinger_discovery(
1527        &self,
1528        client_id: &str,
1529        resource_id: &str,
1530        eventid: Uuid,
1531    ) -> Result<OidcWebfingerResponse, OperationError> {
1532        let mut idms_prox_read = self.idms.proxy_read().await?;
1533        idms_prox_read.oauth2_openid_webfinger(client_id, resource_id)
1534    }
1535
1536    #[instrument(
1537        level = "info",
1538        skip_all,
1539        fields(uuid = ?eventid)
1540    )]
1541    pub async fn handle_oauth2_rfc8414_metadata(
1542        &self,
1543        client_id: String,
1544        eventid: Uuid,
1545    ) -> Result<Oauth2Rfc8414MetadataResponse, OperationError> {
1546        let idms_prox_read = self.idms.proxy_read().await?;
1547        idms_prox_read.oauth2_rfc8414_metadata(&client_id)
1548    }
1549
1550    #[instrument(
1551        level = "info",
1552        skip_all,
1553        fields(uuid = ?eventid)
1554    )]
1555    pub async fn handle_oauth2_openid_publickey(
1556        &self,
1557        client_id: String,
1558        eventid: Uuid,
1559    ) -> Result<JwkKeySet, OperationError> {
1560        let idms_prox_read = self.idms.proxy_read().await?;
1561        idms_prox_read.oauth2_openid_publickey(&client_id)
1562    }
1563
1564    #[instrument(
1565        level = "info",
1566        skip_all,
1567        fields(uuid = ?eventid)
1568    )]
1569    pub async fn handle_list_applinks(
1570        &self,
1571        client_auth_info: ClientAuthInfo,
1572        eventid: Uuid,
1573    ) -> Result<Vec<AppLink>, OperationError> {
1574        let ct = duration_from_epoch_now();
1575        let mut idms_prox_read = self.idms.proxy_read().await?;
1576        let ident = idms_prox_read
1577            .validate_client_auth_info_to_ident(client_auth_info, ct)
1578            .map_err(|e| {
1579                error!("Invalid identity: {:?}", e);
1580                e
1581            })?;
1582
1583        // Nice and easy!
1584        idms_prox_read.list_applinks(&ident)
1585    }
1586
1587    #[instrument(
1588        level = "info",
1589        skip_all,
1590        fields(uuid = ?eventid)
1591    )]
1592    pub async fn handle_auth_valid(
1593        &self,
1594        client_auth_info: ClientAuthInfo,
1595        eventid: Uuid,
1596    ) -> Result<(), OperationError> {
1597        let ct = duration_from_epoch_now();
1598        let mut idms_prox_read = self.idms.proxy_read().await?;
1599
1600        idms_prox_read
1601            .validate_client_auth_info_to_ident(client_auth_info, ct)
1602            .map(|_| ())
1603            .map_err(|e| {
1604                error!("Invalid identity: {:?}", e);
1605                e
1606            })
1607    }
1608
1609    #[instrument(
1610        level = "info",
1611        skip_all,
1612        fields(uuid = ?eventid)
1613    )]
1614    /// Retrieve a public jwk
1615    pub async fn handle_public_jwk_get(
1616        &self,
1617        key_id: String,
1618        eventid: Uuid,
1619    ) -> Result<Jwk, OperationError> {
1620        let mut idms_prox_read = self.idms.proxy_read().await?;
1621
1622        idms_prox_read.jws_public_jwk(key_id.as_str())
1623    }
1624
1625    #[instrument(
1626        level = "info",
1627        skip_all,
1628        fields(uuid = ?eventid)
1629    )]
1630    pub async fn handle_ldaprequest(
1631        &self,
1632        eventid: Uuid,
1633        protomsg: LdapMsg,
1634        uat: Option<LdapBoundToken>,
1635        ip_addr: IpAddr,
1636    ) -> Option<LdapResponseState> {
1637        let res = match ServerOps::try_from(protomsg) {
1638            Ok(server_op) => self
1639                .ldap
1640                .do_op(&self.idms, server_op, uat, ip_addr, eventid)
1641                .await
1642                .unwrap_or_else(|e| {
1643                    error!("do_op failed -> {:?}", e);
1644                    LdapResponseState::Disconnect(DisconnectionNotice::gen(
1645                        LdapResultCode::Other,
1646                        format!("Internal Server Error {:?}", &eventid).as_str(),
1647                    ))
1648                }),
1649            Err(_) => LdapResponseState::Disconnect(DisconnectionNotice::gen(
1650                LdapResultCode::ProtocolError,
1651                format!("Invalid Request {:?}", &eventid).as_str(),
1652            )),
1653        };
1654        Some(res)
1655    }
1656
1657    pub fn domain_info_read(&self) -> DomainInfoRead {
1658        self.idms.domain_read()
1659    }
1660}