Browse Source

Add ldap connector to bitwarden

pull/677/head
unknown 6 years ago
parent
commit
0eaeb2d686
  1. 4
      src/api/identity.rs
  2. 7
      src/config.rs
  3. 89
      src/ldap.rs
  4. 3
      src/main.rs

4
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);

7
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")

89
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<Error>> {
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<Vec<SearchEntry>, Box<Error>> {
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<HashSet<String>, Box<Error>> {
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)
}

3
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() {

Loading…
Cancel
Save