LDAP querying complete

This commit is contained in:
ViViDboarder 2019-03-29 11:18:56 -07:00
parent cd68ef6f46
commit 6d79984a65
6 changed files with 267 additions and 1 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
config.toml

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "bitwarden_rs_ldap"
version = "0.1.0"
authors = ["ViViDboarder <vividboarder@gmail.com>"]
edition = "2018"
[dependencies]
ldap3 = "0.6"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"

View File

@ -1,2 +1,2 @@
# bwrs_ldap_directory
# bitwarden_rs_ldap
LDAP directory connector for bitwarden_rs

View File

@ -0,0 +1,29 @@
version: '3'
services:
ldap:
image: osixia/openldap
ports:
- 389:389
- 636:636
volumes:
- /var/lib/ldap
- /etc/ldap/slapd.d
environment:
LDAP_READONLY_USER: 'true'
LDAP_READONLY_USER_USERNAME: readonly
LDAP_READONLY_USER_PASSWORD: readonly
admin:
image: osixia/phpldapadmin
ports:
- 8001:80
environment:
PHPLDAPADMIN_HTTPS: 'false'
PHPLDAPADMIN_LDAP_HOSTS: ldap
admin-host:
image: osixia/phpldapadmin
ports:
- 80:80
network_mode: "host"
environment:
PHPLDAPADMIN_HTTPS: 'false'
PHPLDAPADMIN_LDAP_HOSTS: 0.0.0.0

126
src/config.rs Normal file
View File

@ -0,0 +1,126 @@
extern crate serde;
use std::env;
use std::fs;
use serde::Deserialize;
pub type Pass = String;
const CONFIG_PATH_DEFAULT: &str = "config.toml";
/// Returns config path from envioronment or a provided default value
pub fn get_config_path() -> String {
match env::var("CONFIG_PATH") {
Ok(config_path) => config_path,
Err(_) => String::from(CONFIG_PATH_DEFAULT),
}
}
/// Reads configuration from file and panics if it can't
pub fn read_config() -> Config {
let config_path = get_config_path();
let contents = fs::read_to_string(&config_path).unwrap_or_else(|_| {
panic!("Failed to open config file at {}", config_path);
});
let config: Config = toml::from_str(contents.as_str()).unwrap_or_else(|_| {
panic!("Failed to parse config file at {}", config_path);
});
config
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
/// Contains all config values for LDAP syncing
pub struct Config {
ldap_host: String,
ldap_scheme: Option<String>,
ldap_ssl: Option<bool>,
ldap_port: Option<u16>,
ldap_bind_dn: String,
ldap_bind_password: Pass,
ldap_search_base_dn: String,
ldap_search_filter: String,
ldap_mail_field: Option<String>,
ldap_sync_interval_seconds: Option<u64>,
}
impl Config {
/// Create a config instance from file
pub fn from_file() -> Config {
read_config()
}
pub fn get_ldap_url(&self) -> String {
format!(
"{}://{}:{}",
self.get_ldap_scheme(),
self.get_ldap_host(),
self.get_ldap_port()
)
}
pub fn get_ldap_host(&self) -> String {
self.ldap_host.clone()
}
pub fn get_ldap_scheme(&self) -> String {
match &self.ldap_scheme {
Some(ldap_scheme) => ldap_scheme.clone(),
None => {
if self.get_ldap_ssl() {
String::from("ldaps")
} else {
String::from("ldap")
}
}
}
}
pub fn get_ldap_ssl(&self) -> bool {
self.ldap_ssl.unwrap_or(false)
}
pub fn get_ldap_port(&self) -> u16 {
match self.ldap_port {
Some(ldap_port) => ldap_port,
None => {
if self.get_ldap_ssl() {
636
} else {
389
}
}
}
}
pub fn get_ldap_bind_dn(&self) -> String {
self.ldap_bind_dn.clone()
}
pub fn get_ldap_bind_password(&self) -> String {
self.ldap_bind_password.clone()
}
pub fn get_ldap_search_base_dn(&self) -> String {
self.ldap_search_base_dn.clone()
}
pub fn get_ldap_search_filter(&self) -> String {
self.ldap_search_filter.clone()
}
pub fn get_ldap_mail_field(&self) -> String {
let default = String::from("mail");
match &self.ldap_mail_field {
Some(mail_field) => mail_field.clone(),
None => default.clone(),
}
}
pub fn get_ldap_sync_interval_seconds(&self) -> u64 {
self.ldap_sync_interval_seconds.unwrap_or(60)
}
}

100
src/main.rs Normal file
View File

@ -0,0 +1,100 @@
extern crate ldap3;
use std::error::Error;
use std::thread::sleep;
use std::time::Duration;
use ldap3::{DerefAliases, LdapConn, Scope, SearchEntry, SearchOptions};
mod config;
fn main() {
let config = config::Config::from_file();
match do_search(&config) {
Ok(_) => (),
Err(e) => println!("{}", e),
}
if let Err(e) = invite_from_ldap(&config) {
println!("{}", e);
}
if let Err(e) = start_sync_loop(&config) {
println!("{}", e);
}
}
/// Creates an LDAP connection, authenticating if necessary
fn ldap_client(ldap_url: String, bind_dn: String, bind_pw: String) -> Result<LdapConn, Box<Error>> {
let ldap = LdapConn::new(ldap_url.as_str())?;
match ldap.simple_bind(bind_dn.as_str(), bind_pw.as_str()) {
_ => {}
};
Ok(ldap)
}
/// Retrieves search results from ldap
fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, Box<Error>> {
let ldap = ldap_client(
config.get_ldap_url(),
config.get_ldap_bind_dn(),
config.get_ldap_bind_password(),
);
let mail_field = config.get_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.get_ldap_search_base_dn().as_str(),
Scope::Subtree,
&config.get_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)
}
/// Perform a simple search and list users
fn do_search(config: &config::Config) -> Result<(), Box<Error>> {
let mail_field = config.get_ldap_mail_field();
let entries = search_entries(config)?;
for user in entries {
println!("{:?}", user);
if let Some(user_email) = user.attrs[mail_field.as_str()].first() {
println!("{}", user_email);
}
}
Ok(())
}
fn invite_from_ldap(config: &config::Config) -> Result<(), Box<Error>> {
let mail_field = config.get_ldap_mail_field();
for ldap_user in search_entries(config)? {
if let Some(user_email) = ldap_user.attrs[mail_field.as_str()].first() {
println!("Try to invite user: {}", user_email);
}
}
Ok(())
}
fn start_sync_loop(config: &config::Config) -> Result<(), Box<Error>> {
let interval = Duration::from_secs(config.get_ldap_sync_interval_seconds());
loop {
invite_from_ldap(config)?;
sleep(interval);
}
}