add support for ldap
This commit is contained in:
@@ -13,6 +13,7 @@ guid-create = "0.1"
|
||||
heck = "0.3.0"
|
||||
itertools = "0.8.0"
|
||||
lazy_static = "1.0"
|
||||
ldap3 = "0.7.1"
|
||||
migrations_internals= "1.4.0"
|
||||
openssl = "0.10.22"
|
||||
rocket = "0.4.5"
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct Config {
|
||||
pub logo: LogoConfig,
|
||||
pub default_theme: String,
|
||||
pub media_directory: String,
|
||||
pub ldap: Option<LdapConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -240,6 +241,40 @@ impl SearchTokenizerConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LdapConfig {
|
||||
pub addr: String,
|
||||
pub base_dn: String,
|
||||
pub tls: bool,
|
||||
pub user_name_attr: String,
|
||||
pub mail_attr: String,
|
||||
}
|
||||
|
||||
fn get_ldap_config() -> Option<LdapConfig> {
|
||||
let addr = var("LDAP_ADDR").ok();
|
||||
let base_dn = var("LDAP_BASE_DN").ok();
|
||||
if addr.is_some() && base_dn.is_some() {
|
||||
let tls = var("LDAP_TLS").unwrap_or_else(|_| "false".to_owned());
|
||||
let tls = match tls.as_ref() {
|
||||
"1" | "true" | "TRUE" => true,
|
||||
"0" | "false" | "FALSE" => false,
|
||||
_ => panic!("Invalid LDAP configuration : tls")
|
||||
};
|
||||
let user_name_attr = var("LDAP_USER_NAME_ATTR").unwrap_or_else(|_| "cn".to_owned());
|
||||
let mail_attr = var("LDAP_USER_MAIL_ATTR").unwrap_or_else(|_| "mail".to_owned());
|
||||
Some(LdapConfig {
|
||||
addr: addr.unwrap(),
|
||||
base_dn: base_dn.unwrap(),
|
||||
tls,
|
||||
user_name_attr,
|
||||
mail_attr,
|
||||
})
|
||||
} else if addr.is_some() && base_dn.is_some() {
|
||||
panic!("Invalid LDAP configuration : both LDAP_ADDR and LDAP_BASE_DN must be set")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: Config = Config {
|
||||
base_url: var("BASE_URL").unwrap_or_else(|_| format!(
|
||||
@@ -267,5 +302,6 @@ lazy_static! {
|
||||
default_theme: var("DEFAULT_THEME").unwrap_or_else(|_| "default-light".to_owned()),
|
||||
media_directory: var("MEDIA_UPLOAD_DIRECTORY")
|
||||
.unwrap_or_else(|_| "static/media".to_owned()),
|
||||
ldap: get_ldap_config(),
|
||||
};
|
||||
}
|
||||
|
||||
+59
-10
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, db_conn::DbConn, follows::Follow,
|
||||
ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, config::CONFIG, db_conn::DbConn, follows::Follow,
|
||||
instance::*, medias::Media, notifications::Notification, post_authors::PostAuthor, posts::Post,
|
||||
safe_string::SafeString, schema::users, search::Searcher, timeline::Timeline, Connection,
|
||||
Error, PlumeRocket, Result, ITEMS_PER_PAGE,
|
||||
@@ -14,6 +14,7 @@ use activitypub::{
|
||||
use bcrypt;
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl};
|
||||
use ldap3::{LdapConn, SearchEntry, Scope};
|
||||
use openssl::{
|
||||
hash::MessageDigest,
|
||||
pkey::{PKey, Private},
|
||||
@@ -292,20 +293,65 @@ impl User {
|
||||
bcrypt::hash(pass, 10).map_err(Error::from)
|
||||
}
|
||||
|
||||
fn ldap_register() -> Result<User> {
|
||||
return Err(Error::NotFound);
|
||||
unimplemented!()
|
||||
fn ldap_register(conn: &Connection, name: &str, password: &str) -> Result<User> {
|
||||
if let Some(ldap) = CONFIG.ldap.as_ref() {
|
||||
let mut ldap_conn = LdapConn::new(&ldap.addr).map_err(|_| Error::NotFound)?;
|
||||
let ldap_name = format!("{}={},{}", ldap.user_name_attr, name, ldap.base_dn);
|
||||
let bind = ldap_conn.simple_bind(&ldap_name, password).map_err(|_| Error::NotFound)?;
|
||||
if bind.success().is_ok() {
|
||||
let search = ldap_conn.search(&ldap_name, Scope::Base, "(|(objectClass=person)(objectClass=user))", vec![&ldap.mail_attr])
|
||||
.map_err(|_| Error::NotFound)?
|
||||
.success()
|
||||
.map_err(|_| Error::NotFound)?;
|
||||
for entry in search.0 {
|
||||
let entry = SearchEntry::construct(entry);
|
||||
let email = entry.attrs.get("mail")
|
||||
.and_then(|vec| vec.first());
|
||||
if email.is_some() {
|
||||
let _ = ldap_conn.unbind();
|
||||
return NewUser::new_local(
|
||||
conn,
|
||||
name.to_owned(),
|
||||
name.to_owned(),
|
||||
Role::Normal,
|
||||
"",
|
||||
email.unwrap().to_owned(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
let _ = ldap_conn.unbind();
|
||||
Err(Error::NotFound)
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn ldap_login(&self) -> bool {
|
||||
return false;
|
||||
fn ldap_login(&self, password: &str) -> bool {
|
||||
if let Some(ldap) = CONFIG.ldap.as_ref() {
|
||||
let mut conn = if let Ok(conn) = LdapConn::new(&ldap.addr) {
|
||||
conn
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
let name = format!("{}={},{}", ldap.user_name_attr, &self.username, ldap.base_dn);
|
||||
if let Ok(bind) = conn.simple_bind(&name, password) {
|
||||
bind.success().is_ok()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn login(conn: &Connection, ident: &str, password: &str) -> Result<User> {
|
||||
let local_id = Instance::get_local()?.id;
|
||||
let user = User::find_by_email(conn, ident)
|
||||
.or_else(|_| User::find_by_name(conn, ident, local_id))
|
||||
.or_else(|_| User::ldap_register())
|
||||
.and_then(|u| if u.instance_id == local_id {
|
||||
Ok(u)
|
||||
} else {
|
||||
@@ -321,13 +367,16 @@ impl User {
|
||||
}
|
||||
},
|
||||
Ok(user) => {
|
||||
if user.ldap_login() {
|
||||
if user.ldap_login(password) {
|
||||
Ok(user)
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
},
|
||||
e => {
|
||||
if let Ok(user) = User::ldap_register(conn, ident, password) {
|
||||
return Ok(user);
|
||||
}
|
||||
let other = User::get(&*conn, 1).expect("No user is registered").hashed_password;
|
||||
other.map(|pass| bcrypt::verify(password, &pass));
|
||||
e
|
||||
@@ -1019,7 +1068,7 @@ impl NewUser {
|
||||
role: Role,
|
||||
summary: &str,
|
||||
email: String,
|
||||
password: String,
|
||||
password: Option<String>,
|
||||
) -> Result<User> {
|
||||
let (pub_key, priv_key) = gen_keypair();
|
||||
let instance = Instance::get_local()?;
|
||||
@@ -1037,7 +1086,7 @@ impl NewUser {
|
||||
summary: summary.to_owned(),
|
||||
summary_html: SafeString::new(&utils::md_to_html(&summary, None, false, None).0),
|
||||
email: Some(email),
|
||||
hashed_password: Some(password),
|
||||
hashed_password: password,
|
||||
instance_id: instance.id,
|
||||
public_key: String::from_utf8(pub_key).or(Err(Error::Signature))?,
|
||||
private_key: Some(String::from_utf8(priv_key).or(Err(Error::Signature))?),
|
||||
|
||||
Reference in New Issue
Block a user