tuwunel_api/client/session/
ldap.rs1use futures::FutureExt;
2use ruma::{OwnedUserId, UserId};
3use tuwunel_core::{Err, Result, debug};
4use tuwunel_service::{Services, users::Register};
5
6use super::password_login;
7
8#[tracing::instrument(skip_all, fields(%user_id), name = "ldap")]
13pub(super) async fn ldap_login(
14 services: &Services,
15 user_id: &UserId,
16 lowercased_user_id: &UserId,
17 password: &str,
18) -> Result<OwnedUserId> {
19 let bind_dn = services
20 .users
21 .ldap_bind_dn(lowercased_user_id.localpart());
22
23 let (user_dn, is_ldap_admin) = match bind_dn {
24 | Some(user_dn) => (user_dn, false),
25 | None => {
26 debug!("Searching user in LDAP");
27
28 let dns = services.users.search_ldap(user_id).await?;
29 if dns.len() >= 2 {
30 return Err!(Ldap("LDAP search returned two or more results"));
31 }
32
33 let Some((user_dn, is_admin)) = dns.first() else {
34 return password_login(services, user_id, lowercased_user_id, password).await;
35 };
36
37 (user_dn.clone(), *is_admin)
38 },
39 };
40
41 let user_id = services
42 .users
43 .auth_ldap(&user_dn, password)
44 .await
45 .map(|()| lowercased_user_id.to_owned())?;
46
47 if !services.users.exists(lowercased_user_id).await {
55 services
56 .users
57 .full_register(Register {
58 user_id: Some(lowercased_user_id),
59 password: Some("*"),
60 origin: Some("ldap"),
61 ..Default::default()
62 })
63 .await?;
64 }
65
66 if !services.config.ldap.admin_filter.is_empty() {
68 let is_tuwunel_admin = services
69 .admin
70 .user_is_admin(lowercased_user_id)
71 .await;
72
73 if is_ldap_admin && !is_tuwunel_admin {
74 services
75 .admin
76 .make_user_admin(lowercased_user_id)
77 .boxed()
78 .await?;
79 } else if !is_ldap_admin && is_tuwunel_admin {
80 services
81 .admin
82 .revoke_admin(lowercased_user_id)
83 .await?;
84 }
85 }
86
87 Ok(user_id)
88}