From 0eaeb2d686797213b3b3d5f62ab9a81765e29b93 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 22 Oct 2019 13:12:46 +0200 Subject: [PATCH] Add ldap connector to bitwarden --- src/api/identity.rs | 4 +- src/config.rs | 7 +--- src/ldap.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 ++ 4 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 src/ldap.rs diff --git a/src/api/identity.rs b/src/api/identity.rs index e2a02ec9..6894661c 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -91,9 +91,9 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult if CONFIG._enable_ldap() { // Extract ldap username from email - let email_parts: Vec<_> = username.split("@").collect(); - let ldap_username = email_parts[0]; + let ldap_username = username.split("@").nth(0).unwrap(); let password = data.password.as_ref().unwrap(); + // Attempt to bind to ldap with these credentials match LdapConn::new(CONFIG.ldap_host().as_str()) { Ok(ldap) => { let bind = ldap.simple_bind(ldap_username, password); diff --git a/src/config.rs b/src/config.rs index 704b3548..e2eb62fc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -368,10 +368,6 @@ make_config! { ldap: _enable_ldap { /// Enabled |> Disabling will prevent users from being able to login through ldap _enable_ldap: bool, true, def, false; - /// Bitwarden URL |> The root URL for accessing bitwarden_rs. Eg: https://bw.example.com - bitwarden_url: String, true, def, String::new(); - /// Bitwarden Admin Token - bitwarden_admin_token: String, true, def, String::new(); /// LDAP Host ldap_host: String, true, def, String::new(); /// LDAP Bind DN @@ -383,13 +379,12 @@ make_config! { /// LDAP Search filter ldap_search_filter: String, true, def, String::new(); /// LDAP Sync interval - ldap_sync_interval: u32, true, def, 10; + ldap_sync_interval: u64, true, def, 10; }, } fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { let db_url = cfg.database_url.to_lowercase(); - if cfg!(feature = "sqlite") { if db_url.starts_with("mysql:") || db_url.starts_with("postgresql:") { err!("`DATABASE_URL` is meant for MySQL or Postgres, while this server is meant for SQLite") diff --git a/src/ldap.rs b/src/ldap.rs new file mode 100644 index 00000000..51c7eab1 --- /dev/null +++ b/src/ldap.rs @@ -0,0 +1,89 @@ +use crate::db; +use crate::CONFIG; +use ldap3::{DerefAliases, LdapConn, Scope, SearchEntry, SearchOptions}; +use std::collections::HashSet; +use std::error::Error; +use std::thread::sleep; +use std::time::Duration; + +pub fn launch_ldap_connector() { + let pool = db::init_pool(); + let conn = db::DbConn(pool.get().expect("Couldn't connect to DB.")); + let interval = Duration::from_secs(CONFIG.ldap_sync_interval()); + loop { + sync_from_ldap(&conn).expect("Couldn't sync users from LDAP."); + sleep(interval); + } +} + +/// Invite all LDAP users to Bitwarden +fn sync_from_ldap(conn: &db::DbConn) -> Result<(), Box> { + match get_existing_users(&conn) { + Ok(existing_users) => { + let mut num_users = 0; + for ldap_user in search_entries()? { + // Safely get first email from list of emails in field + if let Some(user_email) = ldap_user.attrs.get("mail").and_then(|l| (l.first())) { + if existing_users.contains(user_email) { + println!("User with email already exists: {}", user_email); + } else { + println!("Try to add user: {}", user_email); + // Add user + db::models::User::new(user_email.to_string()).save(conn)?; + num_users = num_users + 1; + } + } else { + println!("Warning: Email field, mail, not found on user"); + } + } + + // Maybe think about returning this value for some other use + println!("Added {} user(s).", num_users); + } + Err(e) => { + println!("Error: Failed to get existing users from Bitwarden"); + return Err(e); + } + } + + Ok(()) +} + +/// Retrieves search results from ldap +fn search_entries() -> Result, Box> { + let ldap = LdapConn::new(CONFIG.ldap_host().as_str())?; + ldap.simple_bind(CONFIG.ldap_bind_dn().as_str(), CONFIG.ldap_bind_password().as_str())?; + + let fields = vec!["uid", "givenname", "sn", "cn", "mail"]; + + // TODO: Something something error handling + let (results, _res) = ldap + .with_search_options(SearchOptions::new().deref(DerefAliases::Always)) + .search( + CONFIG.ldap_search_base_dn().as_str(), + Scope::Subtree, + CONFIG.ldap_search_filter().as_str(), + fields, + )? + .success()?; + + // Build list of entries + let mut entries = Vec::new(); + for result in results { + entries.push(SearchEntry::construct(result)); + } + + Ok(entries) +} + +/// Creates set of email addresses for users that already exist in Bitwarden +fn get_existing_users(conn: &db::DbConn) -> Result, Box> { + let all_users = db::models::User::get_all(conn); + + let mut user_emails = HashSet::with_capacity(all_users.len()); + for user in all_users { + user_emails.insert(user.email); + } + + Ok(user_emails) +} diff --git a/src/main.rs b/src/main.rs index d95aa35b..5b7db474 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ mod auth; mod config; mod crypto; mod db; +mod ldap; mod mail; mod util; @@ -55,6 +56,8 @@ fn main() { migrations::run_migrations(); launch_rocket(); + + ldap::launch_ldap_connector(); } fn launch_info() {