mirror of
https://github.com/ViViDboarder/bitwarden_rs_ldap.git
synced 2024-11-25 04:36:27 +00:00
Add checking of both users and invites before sending new ones
This commit is contained in:
parent
57851489cb
commit
d1b467a148
@ -23,6 +23,7 @@ Configuration values are as follows:
|
|||||||
|`ldap_search_filter`|String||Filter used when searching LDAP for users. Eg. `(&(objectClass=*)(uid=*))`|
|
|`ldap_search_filter`|String||Filter used when searching LDAP for users. Eg. `(&(objectClass=*)(uid=*))`|
|
||||||
|`ldap_mail_field`|String|Optional|Field for each user record that contains the email address to use. Defaults to `mail`|
|
|`ldap_mail_field`|String|Optional|Field for each user record that contains the email address to use. Defaults to `mail`|
|
||||||
|`ldap_sync_interval_seconds`|Integer|Optional|Number of seconds to wait between each LDAP request. Defaults to `60`|
|
|`ldap_sync_interval_seconds`|Integer|Optional|Number of seconds to wait between each LDAP request. Defaults to `60`|
|
||||||
|
|`ldap_sync_loop`|Boolean|Optional|Indicates whether or not syncing should be polled in a loop or done once. Defaults to `true`|
|
||||||
|
|
||||||
## Future
|
## Future
|
||||||
|
|
||||||
|
@ -9,19 +9,26 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
const COOKIE_LIFESPAN: Duration = Duration::from_secs(20 * 60);
|
const COOKIE_LIFESPAN: Duration = Duration::from_secs(20 * 60);
|
||||||
|
|
||||||
|
fn true_val() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
Email: String,
|
#[serde(rename = "Email")]
|
||||||
|
email: String,
|
||||||
#[serde(rename = "_Enabled")]
|
#[serde(rename = "_Enabled")]
|
||||||
Enabled: bool,
|
#[serde(default = "true_val")]
|
||||||
|
enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn get_email(&self) -> String {
|
pub fn get_email(&self) -> String {
|
||||||
self.Email.clone()
|
self.email.clone()
|
||||||
}
|
}
|
||||||
pub fn is_enabled(&self) -> bool {
|
pub fn is_enabled(&self) -> bool {
|
||||||
self.Enabled
|
self.enabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,4 +158,18 @@ impl Client {
|
|||||||
let all_users: Vec<User> = self.get("/users").json()?;
|
let all_users: Vec<User> = self.get("/users").json()?;
|
||||||
Ok(all_users)
|
Ok(all_users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get all invited users
|
||||||
|
pub fn invites(&mut self) -> Result<Vec<User>, Box<Error>> {
|
||||||
|
let all_invites: Vec<User> = self.get("/invites").json()?;
|
||||||
|
Ok(all_invites)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all users and invites
|
||||||
|
pub fn users_and_invites(&mut self) -> Result<Vec<User>, Box<Error>> {
|
||||||
|
let mut all_users = self.users()?;
|
||||||
|
let mut invites = self.invites()?;
|
||||||
|
all_users.append(&mut invites);
|
||||||
|
Ok(all_users)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,8 @@ pub struct Config {
|
|||||||
ldap_mail_field: Option<String>,
|
ldap_mail_field: Option<String>,
|
||||||
// Interval syncing config
|
// Interval syncing config
|
||||||
ldap_sync_interval_seconds: Option<u64>,
|
ldap_sync_interval_seconds: Option<u64>,
|
||||||
|
// Should start background sync loop
|
||||||
|
ldap_sync_loop: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -139,4 +141,8 @@ impl Config {
|
|||||||
pub fn get_ldap_sync_interval_seconds(&self) -> u64 {
|
pub fn get_ldap_sync_interval_seconds(&self) -> u64 {
|
||||||
self.ldap_sync_interval_seconds.unwrap_or(60)
|
self.ldap_sync_interval_seconds.unwrap_or(60)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ldap_sync_loop(&self) -> bool {
|
||||||
|
self.ldap_sync_loop.unwrap_or(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
60
src/main.rs
60
src/main.rs
@ -2,7 +2,6 @@ extern crate ldap3;
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::env;
|
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -11,24 +10,6 @@ use ldap3::{DerefAliases, LdapConn, Scope, SearchEntry, SearchOptions};
|
|||||||
mod bw_admin;
|
mod bw_admin;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
/// Container for args parsed from the command line
|
|
||||||
struct ParsedArgs {
|
|
||||||
start_loop: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParsedArgs {
|
|
||||||
pub parse() -> ParsedArgs {
|
|
||||||
let mut parsed_args = ParsedArgs {};
|
|
||||||
for arg in env::args().collect() {
|
|
||||||
if arg == "--loop" {
|
|
||||||
parsed_args.start_loop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed_args.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let config = config::Config::from_file();
|
let config = config::Config::from_file();
|
||||||
let mut client = bw_admin::Client::new(
|
let mut client = bw_admin::Client::new(
|
||||||
@ -36,8 +17,7 @@ fn main() {
|
|||||||
config.get_bitwarden_admin_token().clone(),
|
config.get_bitwarden_admin_token().clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let parsed_args = ParsedArgs::parse();
|
if let Err(e) = invite_users(&config, &mut client, config.get_ldap_sync_loop()) {
|
||||||
if let Err(e) = invite_users(&config, &mut client, parsed_args.start_loop) {
|
|
||||||
panic!("{}", e);
|
panic!("{}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,13 +27,13 @@ fn invite_users(
|
|||||||
config: &config::Config,
|
config: &config::Config,
|
||||||
client: &mut bw_admin::Client,
|
client: &mut bw_admin::Client,
|
||||||
start_loop: bool,
|
start_loop: bool,
|
||||||
) -> Result((), Box<Error>> {
|
) -> Result<(), Box<Error>> {
|
||||||
let user_emails = get_existing_users(&mut client)?;
|
// TODO: Better error handling to differentiate failure to connect to Bitwarden vs LDAP
|
||||||
|
|
||||||
if start_loop {
|
if start_loop {
|
||||||
start_sync_loop(&config, &mut client)?;
|
start_sync_loop(config, client)?;
|
||||||
} else {
|
} else {
|
||||||
invite_from_ldap(&config, &mut client)?;
|
invite_from_ldap(config, client)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -61,10 +41,15 @@ fn invite_users(
|
|||||||
|
|
||||||
/// Creates set of email addresses for users that already exist in Bitwarden
|
/// Creates set of email addresses for users that already exist in Bitwarden
|
||||||
fn get_existing_users(client: &mut bw_admin::Client) -> Result<HashSet<String>, Box<Error>> {
|
fn get_existing_users(client: &mut bw_admin::Client) -> Result<HashSet<String>, Box<Error>> {
|
||||||
let all_users = client.users()?;
|
let all_users = client.users_and_invites()?;
|
||||||
let mut user_emails = HashSet::with_capacity(all_users.len());
|
let mut user_emails = HashSet::with_capacity(all_users.len());
|
||||||
for user in client.users()? {
|
for user in all_users {
|
||||||
user_emails.insert(user.get_email());
|
user_emails.insert(user.get_email());
|
||||||
|
if user.is_enabled() {
|
||||||
|
println!("Existing user or invite found with email: {}", user.get_email());
|
||||||
|
} else {
|
||||||
|
println!("Existing disabled user found with email: {}", user.get_email());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(user_emails)
|
Ok(user_emails)
|
||||||
@ -88,6 +73,10 @@ fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, Box<Error
|
|||||||
config.get_ldap_bind_password(),
|
config.get_ldap_bind_password(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ldap.is_err() {
|
||||||
|
println!("Error: Could not connect to ldap server");
|
||||||
|
}
|
||||||
|
|
||||||
let mail_field = config.get_ldap_mail_field();
|
let mail_field = config.get_ldap_mail_field();
|
||||||
let fields = vec!["uid", "givenname", "sn", "cn", mail_field.as_str()];
|
let fields = vec!["uid", "givenname", "sn", "cn", mail_field.as_str()];
|
||||||
|
|
||||||
@ -116,15 +105,26 @@ fn invite_from_ldap(
|
|||||||
config: &config::Config,
|
config: &config::Config,
|
||||||
client: &mut bw_admin::Client,
|
client: &mut bw_admin::Client,
|
||||||
) -> Result<(), Box<Error>> {
|
) -> Result<(), Box<Error>> {
|
||||||
|
let existing_users = get_existing_users(client)?;
|
||||||
|
|
||||||
let mail_field = config.get_ldap_mail_field();
|
let mail_field = config.get_ldap_mail_field();
|
||||||
|
let mut num_users = 0;
|
||||||
for ldap_user in search_entries(config)? {
|
for ldap_user in search_entries(config)? {
|
||||||
if let Some(user_email) = ldap_user.attrs[mail_field.as_str()].first() {
|
if let Some(user_email) = ldap_user.attrs[mail_field.as_str()].first() {
|
||||||
println!("Try to invite user: {}", user_email);
|
if existing_users.contains(user_email) {
|
||||||
let response = client.invite(user_email);
|
println!("User with email already exists: {}", user_email);
|
||||||
println!("Invite response: {:?}", response);
|
} else {
|
||||||
|
println!("Try to invite user: {}", user_email);
|
||||||
|
let response = client.invite(user_email);
|
||||||
|
num_users = num_users + 1;
|
||||||
|
println!("Invite response: {:?}", response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maybe think about returning this value for some other use
|
||||||
|
println!("Sent invites to {} user(s).", num_users);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user