diff --git a/plume-models/Cargo.toml b/plume-models/Cargo.toml index 6eac4b62..6bb50758 100644 --- a/plume-models/Cargo.toml +++ b/plume-models/Cargo.toml @@ -36,6 +36,8 @@ once_cell = "1.12.0" lettre = "0.9.6" native-tls = "0.2.10" activitystreams = "=0.7.0-alpha.20" +ahash = "=0.8.6" +heck = "0.4.1" [dependencies.chrono] features = ["serde"] diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 478283e5..05efde62 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -1,3 +1,4 @@ +use heck::ToUpperCamelCase; use crate::{ instance::*, medias::Media, posts::Post, safe_string::SafeString, schema::blogs, users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE, @@ -102,9 +103,18 @@ impl Blog { find_by!(blogs, find_by_ap_url, ap_url as &str); find_by!(blogs, find_by_name, actor_id as &str, instance_id as i32); +/// Remove non alphanumeric characters and CamelCase a string + pub fn slug(title: &str) -> String { + title.to_upper_camel_case() + .chars() + .filter(|c| c.is_alphanumeric()) + .collect() + } + /* pub fn slug(title: &str) -> &str { title } + */ pub fn get_instance(&self, conn: &Connection) -> Result { Instance::get(conn, self.instance_id) diff --git a/plume-models/src/config.rs b/plume-models/src/config.rs index 12441a75..a352f0f7 100644 --- a/plume-models/src/config.rs +++ b/plume-models/src/config.rs @@ -293,6 +293,7 @@ pub struct LdapConfig { pub tls: bool, pub user_name_attr: String, pub mail_attr: String, + pub user: Option<(String, String)>, } fn get_ldap_config() -> Option { @@ -304,16 +305,26 @@ fn get_ldap_config() -> Option { let tls = string_to_bool(&tls, "LDAP_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()); + //2023-12-30 + let user = var("LDAP_USER").ok(); + let password = var("LDAP_PASSWORD").ok(); + let user = match (user, password) { + (Some(user), Some(password)) => Some((user, password)), + (None, None) => None, + _ => panic!("Invalid LDAP configuration both or neither of LDAP_USER and LDAP_PASSWORD must be set") + }; + // Some(LdapConfig { addr, base_dn, tls, user_name_attr, mail_attr, + user, }) } (None, None) => None, - (_, _) => { + _ => { panic!("Invalid LDAP configuration : both LDAP_ADDR and LDAP_BASE_DN must be set") } } diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 9658ea9b..e242bc78 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -342,6 +342,42 @@ impl User { bcrypt::hash(pass, 10).map_err(Error::from) } + // [[ LDAP non anonymous bind copied from .... + fn ldap_preconn(ldap_conn: &mut LdapConn) -> Result<()> { + let ldap = CONFIG.ldap.as_ref().unwrap(); + + if let Some((user, password)) = ldap.user.as_ref() { + let bind = ldap_conn + .simple_bind(user, password) + .map_err(|_| Error::NotFound)?; + + if bind.success().is_err() { + return Err(Error::NotFound); + } + } + Ok(()) + } + + fn search_dn(ldap_conn: &mut LdapConn, fil: &str) -> Result { + let mut cn: String = String::new(); + let ldap = CONFIG.ldap.as_ref().unwrap(); + let (rs, _res) = ldap_conn.search( + &ldap.base_dn, + Scope::Subtree, + fil, + vec!["cn"] + ) + .map_err(|_| Error::NotFound)? + .success() + .map_err(|_| Error::NotFound)?; + for entry in rs { + println!("{:?}", SearchEntry::construct(entry.clone()).dn); + cn = SearchEntry::construct(entry).dn; + } + + Ok(cn.to_owned()) + } + fn ldap_register(conn: &Connection, name: &str, password: &str) -> Result { if CONFIG.ldap.is_none() { return Err(Error::NotFound); @@ -349,7 +385,12 @@ impl User { let ldap = CONFIG.ldap.as_ref().unwrap(); let mut ldap_conn = LdapConn::new(&ldap.addr).map_err(|_| Error::NotFound)?; - let ldap_name = format!("{}={},{}", ldap.user_name_attr, name, ldap.base_dn); + + User::ldap_preconn(&mut ldap_conn)?; + + let _filter = format!("(&(objectClass=*)(uid={}))", name).to_owned(); + let ldap_name = User::search_dn(&mut ldap_conn, &_filter).unwrap(); + //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)?; @@ -395,10 +436,18 @@ impl User { } else { return false; }; + if User::ldap_preconn(&mut conn).is_err() { + return false; + } + // + let _filter = format!("(&(objectClass=*)(uid={}))", &self.username).to_owned(); + let name = User::search_dn(&mut conn, &_filter).unwrap(); + /* 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 { @@ -445,7 +494,7 @@ impl User { } // if no user was found, and we were unable to auto-register from ldap // fake-verify a password, and return an error. - let other = User::get(conn, 1) + let other = User::get(&*conn, 1) .expect("No user is registered") .hashed_password; other.map(|pass| bcrypt::verify(password, &pass)); @@ -453,7 +502,7 @@ impl User { } } } - + // ... ldap-non-anon PR https://git.joinplu.me/Plume/Plume/src/branch/ldap-non-anon ]]. pub fn reset_password(&self, conn: &Connection, pass: &str) -> Result<()> { diesel::update(self) .set(users::hashed_password.eq(User::hash_pass(pass)?)) diff --git a/rust-toolchain b/rust-toolchain index b57117c9..c88f9562 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2022-07-19 +nightly-2023-04-14 diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 295d08a8..763ae613 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -101,7 +101,7 @@ pub fn create( Ok(_) => ValidationErrors::new(), Err(e) => e, }; - if Blog::find_by_fqn(&conn, slug).is_ok() { + if Blog::find_by_fqn(&conn, &slug).is_ok() { errors.add( "title", ValidationError { @@ -122,7 +122,7 @@ pub fn create( let blog = Blog::insert( &conn, NewBlog::new_local( - slug.into(), + slug.clone().into(), form.title.to_string(), String::from(""), Instance::get_local()