tuwunel_service/users/
ldap.rs1#![cfg(feature = "ldap")]
2
3use std::collections::HashMap;
4
5use ldap3::{LdapConnAsync, Scope, SearchEntry};
6use ruma::UserId;
7use tuwunel_core::{Result, debug, err, error, implement, result::LogErr, trace};
8
9#[implement(super::Service)]
14pub async fn search_ldap(&self, user_id: &UserId) -> Result<Vec<(String, bool)>> {
15 let localpart = user_id.localpart().to_owned();
16 let lowercased_localpart = localpart.to_lowercase();
17
18 let config = &self.services.server.config.ldap;
19 let uri = config
20 .uri
21 .as_ref()
22 .ok_or_else(|| err!(Ldap(error!("LDAP URI is not configured."))))?;
23
24 if uri.scheme().starts_with("ldaps") {
25 self.services.globals.init_rustls_provider()?;
26 }
27
28 debug!(?uri, "LDAP creating connection...");
29 let (conn, mut ldap) = LdapConnAsync::new(uri.as_str())
30 .await
31 .map_err(|e| err!(Ldap(error!(?user_id, "LDAP connection setup error: {e}"))))?;
32
33 let driver = self.services.server.runtime().spawn(async move {
34 match conn.drive().await {
35 | Err(e) => error!("LDAP connection error: {e}"),
36 | Ok(()) => debug!("LDAP connection completed."),
37 }
38 });
39
40 match (&config.bind_dn, &config.bind_password_file) {
41 | (Some(bind_dn), Some(bind_password_file)) => {
42 let bind_pw = String::from_utf8(std::fs::read(bind_password_file)?)?;
43 ldap.simple_bind(bind_dn, bind_pw.trim())
44 .await
45 .and_then(ldap3::LdapResult::success)
46 .map_err(|e| err!(Ldap(error!("LDAP bind error: {e}"))))?;
47 },
48 | (..) => {},
49 }
50
51 let attr = [&config.uid_attribute, &config.name_attribute];
52
53 let user_filter = &config
54 .filter
55 .replace("{username}", &lowercased_localpart);
56
57 let (entries, _result) = ldap
58 .search(&config.base_dn, Scope::Subtree, user_filter, &attr)
59 .await
60 .and_then(ldap3::SearchResult::success)
61 .inspect(|(entries, result)| trace!(?entries, ?result, "LDAP Search"))
62 .map_err(|e| err!(Ldap(error!(?attr, ?user_filter, "LDAP search error: {e}"))))?;
63
64 let mut dns: HashMap<String, bool> = entries
65 .into_iter()
66 .filter_map(|entry| {
67 let search_entry = SearchEntry::construct(entry);
68 debug!(?search_entry, "LDAP search entry");
69 search_entry
70 .attrs
71 .get(&config.uid_attribute)
72 .into_iter()
73 .chain(search_entry.attrs.get(&config.name_attribute))
74 .any(|ids| ids.contains(&localpart) || ids.contains(&lowercased_localpart))
75 .then_some((search_entry.dn, false))
76 })
77 .collect();
78
79 if !config.admin_filter.is_empty() {
80 let admin_base_dn = if config.admin_base_dn.is_empty() {
81 &config.base_dn
82 } else {
83 &config.admin_base_dn
84 };
85
86 let admin_filter = &config
87 .admin_filter
88 .replace("{username}", &lowercased_localpart);
89
90 let (admin_entries, _result) = ldap
91 .search(admin_base_dn, Scope::Subtree, admin_filter, &attr)
92 .await
93 .and_then(ldap3::SearchResult::success)
94 .inspect(|(entries, result)| trace!(?entries, ?result, "LDAP Admin Search"))
95 .map_err(|e| {
96 err!(Ldap(error!(?attr, ?admin_filter, "Ldap admin search error: {e}")))
97 })?;
98
99 dns.extend(admin_entries.into_iter().filter_map(|entry| {
100 let search_entry = SearchEntry::construct(entry);
101 debug!(?search_entry, "LDAP search entry");
102 search_entry
103 .attrs
104 .get(&config.uid_attribute)
105 .into_iter()
106 .chain(search_entry.attrs.get(&config.name_attribute))
107 .any(|ids| ids.contains(&localpart) || ids.contains(&lowercased_localpart))
108 .then_some((search_entry.dn, true))
109 }));
110 }
111
112 ldap.unbind()
113 .await
114 .map_err(|e| err!(Ldap(error!("LDAP unbind error: {e}"))))?;
115
116 driver.await.log_err().ok();
117
118 Ok(dns.drain().collect())
119}
120
121#[implement(super::Service)]
122pub async fn auth_ldap(&self, user_dn: &str, password: &str) -> Result {
123 let config = &self.services.server.config.ldap;
124 let uri = config
125 .uri
126 .as_ref()
127 .ok_or_else(|| err!(Ldap(error!("LDAP URI is not configured."))))?;
128
129 if uri.scheme().starts_with("ldaps") {
130 self.services.globals.init_rustls_provider()?;
131 }
132
133 debug!(?uri, "LDAP creating connection...");
134 let (conn, mut ldap) = LdapConnAsync::new(uri.as_str())
135 .await
136 .map_err(|e| err!(Ldap(error!(?user_dn, "LDAP connection setup error: {e}"))))?;
137
138 let driver = self.services.server.runtime().spawn(async move {
139 match conn.drive().await {
140 | Err(e) => error!("LDAP connection error: {e}"),
141 | Ok(()) => debug!("LDAP connection completed."),
142 }
143 });
144
145 ldap.simple_bind(user_dn, password)
146 .await
147 .and_then(ldap3::LdapResult::success)
148 .map_err(|e| err!(Request(Forbidden(debug_error!("LDAP authentication error: {e}")))))?;
149
150 ldap.unbind()
151 .await
152 .map_err(|e| err!(Ldap(error!("LDAP unbind error: {e}"))))?;
153
154 driver.await.log_err().ok();
155
156 Ok(())
157}