features, cleanup and bug fixes #26

Merged
ben merged 30 commits from ben/Jobboerse:main into main 2022-06-09 17:36:55 +02:00
4 changed files with 133 additions and 94 deletions
Showing only changes of commit 36ba2f2ff1 - Show all commits

refactor the login handling

Bennet Bleßmann 2022-05-28 01:06:36 +02:00 committed by Bennet Bleßmann
Signed by: ben
GPG key ID: 3BE1A1A3CBC3CF99

View file

@ -1,8 +1,6 @@
use std::collections::HashMap;
use actix_session::Session;
use ldap3::{DerefAliases, SearchOptions};
use log::{debug, warn};
use serde::Deserialize;
mod login_provider;
@ -31,7 +29,7 @@ struct LoginData {
}
impl User {
pub(crate) fn current(session: &Session) -> actix_web::Result<User, LoginRequired> {
pub(crate) fn current(session: &Session) -> Result<User, LoginRequired> {
session
.get::<String>(USER_NAME)
.map_err(|_| LoginRequired::new())
@ -53,100 +51,19 @@ impl User {
provided_password: &str,
session: &Session,
config: &ServerConfig,
) -> actix_web::Result<Option<User>, AuthenticationError> {
const USERNAME_PATTERN: &str = "%{username}";
let result = match &config.config.login_provider {
LoginProviderConfig::Ldap(ldap_config) => {
let ldap_settings =
ldap3::LdapConnSettings::new().set_starttls(ldap_config.starttls);
let (connection, mut ldap) =
ldap3::LdapConnAsync::with_settings(ldap_settings, &ldap_config.server_address)
.await?;
ldap3::drive!(connection);
let escaped_username_for_dn = ldap3::dn_escape(user_name);
let ldap_dn = ldap_config
.ldap_user_dn
.replace(USERNAME_PATTERN, &escaped_username_for_dn);
debug!("Attempting ldap simple bind for fn {}", ldap_dn);
let result = ldap.simple_bind(&ldap_dn, provided_password).await?;
match result.success() {
Ok(_) => {
let escaped_username_for_filter = ldap3::ldap_escape(user_name);
let ldap_user_filter = ldap_config
.ldap_user_filter
.replace(USERNAME_PATTERN, &escaped_username_for_filter);
debug!("Attempting ldap search: {}", ldap_user_filter);
let (search_result, _) = match ldap
.with_search_options(
SearchOptions::default().deref(DerefAliases::Never),
)
.search(&ldap_dn, ldap3::Scope::Subtree, &ldap_user_filter, ["uid"])
) -> Result<Option<User>, AuthenticationError> {
if let Some(user) = config
.config
.login_provider
.authenticate(user_name, provided_password)
.await?
.success()
{
Ok(result) => result,
Err(err) => {
debug!("ldap search failed after successful bind {:?}", err);
return Err(err.into());
}
};
match search_result.len() {
1 => Some(User {
name: user_name.to_owned(),
}),
0 => {
warn!("No ldap entry fount after filter!");
None
}
_ => {
warn!("Multiple ldap entries found!");
None
}
}
}
Err(err) => {
debug!("ldap bind failed: {:?}", err);
None
}
}
}
LoginProviderConfig::Simple(config) => {
let simple_login_data = tokio::fs::read_to_string(&config.file_path).await?;
let login_data: LoginData = toml::from_str(&simple_login_data)?;
if let Some(stored_password) = login_data.users.get(user_name) {
if stored_password == provided_password {
Some(User {
name: user_name.to_owned(),
})
} else {
None
}
} else {
None
}
}
LoginProviderConfig::Disabled => None,
#[cfg(feature = "dev_mode")]
LoginProviderConfig::Development => {
warn!("Using dev-login provider!");
Some(User {
name: user_name.into(),
})
}
};
if let Some(user) = &result {
session
.insert(USER_NAME, &user.name)
.map_err(AuthenticationError::SetCookie)?;
Ok(Some(user))
} else {
Ok(None)
}
Ok(result)
}
}

View file

@ -1,7 +1,10 @@
use log::warn;
use serde::{Deserialize, Serialize};
use crate::auth::login_provider::ldap::LdapLoginProviderConfig;
use crate::auth::login_provider::simple::SimpleLoginProviderConfig;
use crate::auth::User;
use crate::error::AuthenticationError;
mod ldap;
mod simple;
@ -22,6 +25,33 @@ pub(crate) enum LoginProviderConfig {
Development,
}
impl LoginProviderConfig {
pub(crate) async fn authenticate(
&self,
user_name: &str,
provided_password: &str,
) -> Result<Option<User>, AuthenticationError> {
match self {
LoginProviderConfig::Ldap(ldap_config) => {
ldap_config.authenticate(user_name, provided_password).await
}
LoginProviderConfig::Simple(simple_config) => {
simple_config
.authenticate(user_name, provided_password)
.await
}
LoginProviderConfig::Disabled => Ok(None),
#[cfg(feature = "dev_mode")]
LoginProviderConfig::Development => {
warn!("Using dev-login provider!");
Ok(Some(User {
name: user_name.to_owned(),
}))
}
}
}
}
impl Default for LoginProviderConfig {
fn default() -> Self {
LoginProviderConfig::Simple(SimpleLoginProviderConfig::default())

View file

@ -1,3 +1,7 @@
use crate::auth::User;
use crate::error::AuthenticationError;
use ldap3::{DerefAliases, SearchOptions};
use log::{debug, warn};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
@ -11,3 +15,67 @@ pub(crate) struct LdapLoginProviderConfig {
#[serde(default = "super::default_true")]
pub(crate) starttls: bool,
}
impl LdapLoginProviderConfig {
pub(crate) async fn authenticate(
&self,
user_name: &str,
provided_password: &str,
) -> Result<Option<User>, AuthenticationError> {
const USERNAME_PATTERN: &str = "%{username}";
let ldap_settings = ldap3::LdapConnSettings::new().set_starttls(self.starttls);
let (connection, mut ldap) =
ldap3::LdapConnAsync::with_settings(ldap_settings, &self.server_address).await?;
ldap3::drive!(connection);
let escaped_username_for_dn = ldap3::dn_escape(user_name);
let ldap_dn = self
.ldap_user_dn
.replace(USERNAME_PATTERN, &escaped_username_for_dn);
debug!("Attempting ldap simple bind for fn {}", ldap_dn);
let result = ldap.simple_bind(&ldap_dn, provided_password).await?;
Ok(match result.success() {
Ok(_) => {
let escaped_username_for_filter = ldap3::ldap_escape(user_name);
let ldap_user_filter = self
.ldap_user_filter
.replace(USERNAME_PATTERN, &escaped_username_for_filter);
debug!("Attempting ldap search: {}", ldap_user_filter);
let (search_result, _) = match ldap
.with_search_options(SearchOptions::default().deref(DerefAliases::Never))
.search(&ldap_dn, ldap3::Scope::Subtree, &ldap_user_filter, ["uid"])
.await?
.success()
{
Ok(result) => result,
Err(err) => {
debug!("ldap search failed after successful bind {:?}", err);
return Err(err.into());
}
};
match search_result.len() {
1 => Some(User {
name: user_name.to_owned(),
}),
0 => {
warn!("No ldap entry fount after filter!");
None
}
_ => {
warn!("Multiple ldap entries found!");
None
}
}
}
Err(err) => {
debug!("ldap bind failed: {:?}", err);
None
}
})
}
}

View file

@ -1,5 +1,7 @@
use std::path::{Path, PathBuf};
use crate::auth::{LoginData, User};
use crate::error::AuthenticationError;
use log::warn;
use serde::{Deserialize, Serialize};
@ -30,6 +32,28 @@ impl SimpleLoginProviderConfig {
PathBuf::from("./login.toml")
}
}
pub(crate) async fn authenticate(
&self,
user_name: &str,
provided_password: &str,
) -> Result<Option<User>, AuthenticationError> {
let simple_login_data = tokio::fs::read_to_string(&self.file_path).await?;
let login_data: LoginData = toml::from_str(&simple_login_data)?;
Ok(
if let Some(stored_password) = login_data.users.get(user_name) {
if stored_password == provided_password {
Some(User {
name: user_name.to_owned(),
})
} else {
None
}
} else {
None
},
)
}
}
impl Default for SimpleLoginProviderConfig {