features, cleanup and bug fixes #26
4 changed files with 133 additions and 94 deletions
refactor the login handling
commit
36ba2f2ff1
105
src/auth.rs
105
src/auth.rs
|
|
@ -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"])
|
||||
.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 {
|
||||
) -> Result<Option<User>, AuthenticationError> {
|
||||
if let Some(user) = config
|
||||
.config
|
||||
.login_provider
|
||||
.authenticate(user_name, provided_password)
|
||||
.await?
|
||||
{
|
||||
session
|
||||
.insert(USER_NAME, &user.name)
|
||||
.map_err(AuthenticationError::SetCookie)?;
|
||||
Ok(Some(user))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue