From 2de6ba60010e9aae35d80bb8cc1a356c27e58422 Mon Sep 17 00:00:00 2001 From: ViViDboarder Date: Wed, 13 Feb 2019 16:02:33 -0800 Subject: [PATCH] Woo! Got db connections --- Cargo.toml | 3 ++ src/config.rs | 30 +++++++++++++++++ src/ldap.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 src/ldap.rs diff --git a/Cargo.toml b/Cargo.toml index e1df067..3187eba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,6 +99,9 @@ handlebars = "1.1.0" soup = "0.3.0" regex = "1.1.0" +# LDAP libraries +ldap3 = "0.6" + [patch.crates-io] # Add support for Timestamp type rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' } diff --git a/src/config.rs b/src/config.rs index d2aaf03..dbf991a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -286,6 +286,28 @@ make_config! { /// Password smtp_password: Pass, true, option; }, + + /// LDAP settings + ldap: _enable_ldap { + /// Enabled + _enable_ldap: bool, true, def, true; + /// Host + ldap_host: String, true, option; + /// Enable SSL + ldap_ssl: bool, true, def, false; + /// Port + ldap_port: u16, true, auto, |c| if c.ldap_ssl {636} else {389}; + /// Bind dn + ldap_bind_dn: String, true, option; + /// Bind password + ldap_bind_password: Pass, true, option; + /// Search base dn + ldap_search_base_dn: String, true, option; + /// Search filter + ldap_search_filter: String, true, def, "(&(objectClass=*)(uid=*))".to_string(); + /// Email field + ldap_mail_field: String, true, def, "mail".to_string(); + }, } fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { @@ -301,6 +323,10 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { err!("Both `SMTP_USERNAME` and `SMTP_PASSWORD` need to be set to enable email authentication") } + if cfg.ldap_bind_dn.is_some() != cfg.ldap_bind_password.is_some() { + err!("Both `LDAP_BIND_DN` and `LDAP_BIND_PASSWORD` need to be set to enable ldap authentication") + } + Ok(()) } @@ -398,6 +424,10 @@ impl Config { let inner = &self.inner.read().unwrap().config; inner._enable_yubico && inner.yubico_client_id.is_some() && inner.yubico_secret_key.is_some() } + pub fn ldap_enabled(&self) -> bool { + let inner = &self.inner.read().unwrap().config; + inner._enable_ldap && inner.ldap_host.is_some() && inner.ldap_search_base_dn.is_some() + } pub fn render_template( &self, diff --git a/src/ldap.rs b/src/ldap.rs new file mode 100644 index 0000000..3f4497d --- /dev/null +++ b/src/ldap.rs @@ -0,0 +1,90 @@ +extern crate ldap3; + +use std::error::Error; + +use ldap3::{DerefAliases, LdapConn, Scope, SearchEntry, SearchOptions}; + +use crate::db::models::User; +use crate::db::DbConn; +use crate::CONFIG; + +fn main() { + match do_search() { + Ok(_) => (), + Err(e) => println!("{}", e), + } +} + +/// Creates an LDAP connection, authenticating if necessary +fn ldap_client() -> Result> { + let scheme = if CONFIG.ldap_ssl() { "ldaps" } else { "ldap" }; + let host = CONFIG.ldap_host().unwrap(); + let port = CONFIG.ldap_port().to_string(); + + let ldap = LdapConn::new(&format!("{}://{}:{}", scheme, host, port))?; + + match (&CONFIG.ldap_bind_dn(), &CONFIG.ldap_bind_password()) { + (Some(bind_dn), Some(pass)) => { + match ldap.simple_bind(bind_dn, pass) { + _ => {} + }; + } + (_, _) => {} + }; + + Ok(ldap) +} + +/// Retrieves search results from ldap +fn search_entries() -> Result, Box> { + let ldap = ldap_client()?; + + let mail_field = CONFIG.ldap_mail_field(); + let fields = vec!["uid", "givenname", "sn", "cn", mail_field.as_str()]; + + // TODO: Something something error handling + let (results, _res) = ldap + .with_search_options(SearchOptions::new().deref(DerefAliases::Always)) + .search( + &CONFIG.ldap_search_base_dn().unwrap(), + Scope::Subtree, + &CONFIG.ldap_search_filter(), + fields, + )? + .success()?; + + // Build list of entries + let mut entries = Vec::new(); + for result in results { + entries.push(SearchEntry::construct(result)); + } + + Ok(entries) +} + +pub fn do_search() -> Result<(), Box> { + let mail_field = CONFIG.ldap_mail_field(); + let entries = search_entries()?; + for user in entries { + println!("{:?}", user); + if let Some(user_email) = user.attrs[mail_field.as_str()].first() { + println!("{}", user_email); + } + } + + Ok(()) +} + +pub fn invite_from_ldap(conn: DbConn) -> Result<(), Box> { + let mail_field = CONFIG.ldap_mail_field(); + for ldap_user in search_entries()? { + if let Some(user_email) = ldap_user.attrs[mail_field.as_str()].first() { + let user = match User::find_by_mail(user_email.as_str(), &conn) { + Some(user) => println!("User already exists with email: {}", user_email), + None => println!("New user, should add to invites: {}", user_email), + }; + } + } + + Ok(()) +}