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 (user_dn, is_ldap_admin) = match services.config.ldap.bind_dn.as_ref() {
20 | Some(bind_dn) if bind_dn.contains("{username}") =>
21 (bind_dn.replace("{username}", lowercased_user_id.localpart()), false),
22 | _ => {
23 debug!("Searching user in LDAP");
24
25 let dns = services.users.search_ldap(user_id).await?;
26 if dns.len() >= 2 {
27 return Err!(Ldap("LDAP search returned two or more results"));
28 }
29
30 let Some((user_dn, is_admin)) = dns.first() else {
31 return password_login(services, user_id, lowercased_user_id, password).await;
32 };
33
34 (user_dn.clone(), *is_admin)
35 },
36 };
37
38 let user_id = services
39 .users
40 .auth_ldap(&user_dn, password)
41 .await
42 .map(|()| lowercased_user_id.to_owned())?;
43
44 if !services.users.exists(lowercased_user_id).await {
52 services
53 .users
54 .full_register(Register {
55 user_id: Some(lowercased_user_id),
56 password: Some("*"),
57 origin: Some("ldap"),
58 ..Default::default()
59 })
60 .await?;
61 }
62
63 if !services.config.ldap.admin_filter.is_empty() {
65 let is_tuwunel_admin = services
66 .admin
67 .user_is_admin(lowercased_user_id)
68 .await;
69
70 if is_ldap_admin && !is_tuwunel_admin {
71 services
72 .admin
73 .make_user_admin(lowercased_user_id)
74 .boxed()
75 .await?;
76 } else if !is_ldap_admin && is_tuwunel_admin {
77 services
78 .admin
79 .revoke_admin(lowercased_user_id)
80 .await?;
81 }
82 }
83
84 Ok(user_id)
85}