diff options
Diffstat (limited to 'src/datastructures.rs')
| -rw-r--r-- | src/datastructures.rs | 160 |
1 files changed, 38 insertions, 122 deletions
diff --git a/src/datastructures.rs b/src/datastructures.rs index 7f062b3..8b8637f 100644 --- a/src/datastructures.rs +++ b/src/datastructures.rs @@ -18,17 +18,12 @@ use anyhow::Result; use argon2::{ Argon2, - password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng}, + password_hash::{PasswordHasher, PasswordVerifier, phc::PasswordHash}, }; use base64::Engine; -#[cfg(feature = "pam")] -pub use ds_pam::*; use log::error; -use rand::Rng; -use serde::{Deserialize, Serialize}; +use rand::RngExt; use sqlx::ConnectOptions; -#[cfg(feature = "pam")] -use std::borrow::BorrowMut; use std::borrow::Cow; use std::fmt::{Debug, Formatter}; use std::fs::read_to_string; @@ -40,6 +35,9 @@ use url::form_urlencoded; const DEFAULT_CONFIG_LOCATION: &str = "/etc/cgitrc"; const DEFAULT_COOKIE_TTL: u64 = 1200; const DEFAULT_DATABASE_LOCATION: &str = "/etc/cgit/auth.db"; +pub(crate) const DEFAULT_REDIS_URL: &str = "redis://127.0.0.1/"; +const DEFAULT_MAX_LOGIN_ATTEMPTS: u64 = 5; +const DEFAULT_LOGIN_TIMEOUT: u64 = 900; pub const CACHE_DIR: &str = "/var/cache/cgit"; pub type RandIntType = u32; pub const COOKIE_LENGTH: usize = 32; @@ -73,75 +71,25 @@ pub fn rand_str(len: usize) -> String { password } -pub(crate) trait TestSuite { - fn generate_test_config() -> Self; +pub(crate) async fn get_redis_connection( + redis_url: &str, +) -> Result<redis::aio::MultiplexedConnection> { + let client = redis::Client::open(redis_url)?; + Ok(client.get_multiplexed_async_connection().await?) } -#[cfg(feature = "pam")] -pub mod ds_pam { - - #[derive(Debug, Clone)] - pub struct PAMConfig { - use_pam: bool, - provider: String, - } - - impl From<&str> for PAMConfig { - fn from(s: &str) -> Self { - let use_pam = !s.to_lowercase().eq("false"); - Self { - use_pam, - provider: s.to_string(), - } - } - } - - impl PAMConfig { - pub fn get_enabled(&self) -> bool { - self.use_pam - } - - pub fn get_provider(&self) -> &String { - &self.provider - } - } - - impl Default for PAMConfig { - fn default() -> Self { - Self { - use_pam: false, - provider: "system-auth".to_string(), - } - } - } - - #[derive(Debug, Clone)] - pub struct PAMAuthorizer { - provider: String, - } - - impl PAMAuthorizer { - pub fn provider(&self) -> &str { - &self.provider - } - } - - impl From<&PAMConfig> for PAMAuthorizer { - fn from(cfg: &PAMConfig) -> Self { - Self { - provider: cfg.get_provider().clone(), - } - } - } +pub(crate) trait TestSuite { + fn generate_test_config() -> Self; } #[derive(Debug, Clone)] pub struct Config { pub cookie_ttl: u64, database: String, + pub redis_url: String, pub bypass_root: bool, - #[cfg(feature = "pam")] - pam_config: PAMConfig, + pub max_login_attempts: u64, + pub login_timeout: u64, pub(crate) test: bool, protect_config: ProtectSettings, } @@ -151,9 +99,10 @@ impl Default for Config { Self { cookie_ttl: DEFAULT_COOKIE_TTL, database: DEFAULT_DATABASE_LOCATION.to_string(), + redis_url: DEFAULT_REDIS_URL.to_string(), bypass_root: false, - #[cfg(feature = "pam")] - pam_config: Default::default(), + max_login_attempts: DEFAULT_MAX_LOGIN_ATTEMPTS, + login_timeout: DEFAULT_LOGIN_TIMEOUT, test: false, protect_config: Default::default(), } @@ -170,11 +119,12 @@ impl Config { let mut cookie_ttl: u64 = DEFAULT_COOKIE_TTL; let mut database: &str = "/etc/cgit/auth.db"; + let mut redis_url: &str = DEFAULT_REDIS_URL; let mut bypass_root: bool = false; + let mut max_login_attempts: u64 = DEFAULT_MAX_LOGIN_ATTEMPTS; + let mut login_timeout: u64 = DEFAULT_LOGIN_TIMEOUT; let mut protect_enabled: bool = true; let mut protect_white_list_mode: bool = true; - #[cfg(feature = "pam")] - let mut use_pam: &str = "false"; //let mut skip_user_access_check: bool = false; for line in file.lines() { @@ -193,9 +143,12 @@ impl Config { match key_name { "cookie-ttl" => cookie_ttl = value.parse().unwrap_or(DEFAULT_COOKIE_TTL), "database" => database = value, + "redis-url" => redis_url = value, "bypass-root" => bypass_root = value.to_lowercase().eq("true"), - #[cfg(feature = "pam")] - "use-pam" => use_pam = value, + "max-login-attempts" => { + max_login_attempts = value.parse().unwrap_or(DEFAULT_MAX_LOGIN_ATTEMPTS) + } + "login-timeout" => login_timeout = value.parse().unwrap_or(DEFAULT_LOGIN_TIMEOUT), "protect" => match value.to_lowercase().as_str() { "full" => { protect_enabled = true; @@ -217,10 +170,11 @@ impl Config { Self { cookie_ttl, database: database.to_string(), + redis_url: redis_url.to_string(), bypass_root, + max_login_attempts, + login_timeout, - #[cfg(feature = "pam")] - pam_config: PAMConfig::from(use_pam), test: false, protect_config: ProtectSettings::from_path( protect_enabled, @@ -307,11 +261,6 @@ impl Config { self.protect_config.query_is_all_protected() } - #[cfg(feature = "pam")] - fn get_pam_config(&self) -> &PAMConfig { - &self.pam_config - } - pub fn get_test_status(&self) -> bool { self.test } @@ -321,10 +270,11 @@ impl TestSuite for Config { fn generate_test_config() -> Self { Self { database: "test/tmp.db".to_string(), + redis_url: DEFAULT_REDIS_URL.to_string(), bypass_root: false, cookie_ttl: DEFAULT_COOKIE_TTL, - #[cfg(feature = "pam")] - pam_config: Default::default(), + max_login_attempts: DEFAULT_MAX_LOGIN_ATTEMPTS, + login_timeout: DEFAULT_LOGIN_TIMEOUT, test: true, protect_config: ProtectSettings::generate_test_config(), } @@ -505,11 +455,10 @@ impl FormData { pub fn gen_string_argon2_hash(s: &str) -> Result<String> { let passwd = s.as_bytes(); - let salt = SaltString::generate(&mut OsRng); let argon2_alg = Argon2::default(); - Ok(argon2_alg.hash_password(passwd, &salt).unwrap().to_string()) + Ok(argon2_alg.hash_password(passwd).unwrap().to_string()) } pub fn set_password(&mut self, password: String) { @@ -562,12 +511,12 @@ impl From<String> for FormData { } } -#[derive(Serialize, Deserialize)] +/* #[derive(Serialize, Deserialize)] struct IvFile { iv: String, timestamp: u64, } - + */ #[derive(Debug)] pub struct Cookie { timestamp: u64, @@ -589,7 +538,9 @@ impl Cookie { pub fn load_from_request(cookies: &str) -> Result<Option<Self>> { let mut cookie_self = None; for cookie in cookies.split(';').map(|x| x.trim()) { - let (key, value) = cookie.split_once('=').unwrap(); + let Some((key, value)) = cookie.split_once('=') else { + continue; + }; if key.eq("cgit_auth") { let value = base64::engine::general_purpose::STANDARD .decode(value) @@ -651,8 +602,6 @@ impl std::fmt::Display for Cookie { #[derive(Debug, Clone)] pub enum AuthorizerType { - #[cfg(feature = "pam")] - PAM, Password, } @@ -662,8 +611,6 @@ impl std::fmt::Display for AuthorizerType { f, "{}", match self { - #[cfg(feature = "pam")] - AuthorizerType::PAM => "PAM", AuthorizerType::Password => "PASSWORD", } ) @@ -698,7 +645,6 @@ pub struct WrapConfigure { } impl From<Config> for WrapConfigure { - #[cfg(not(feature = "pam"))] fn from(cfg: Config) -> Self { let authorizer = Box::new(SQLAuthorizer::from(&cfg)); Self { @@ -706,36 +652,6 @@ impl From<Config> for WrapConfigure { authorizer, } } - #[cfg(feature = "pam")] - fn from(cfg: Config) -> Self { - let authorizer: Box<dyn Authorizer> = if cfg.get_pam_config().get_enabled() { - Box::new(PAMAuthorizer::from(cfg.get_pam_config())) - } else { - Box::new(SQLAuthorizer::from(&cfg)) - }; - Self { - config: cfg, - authorizer, - } - } -} - -#[cfg(feature = "pam")] -#[async_trait::async_trait] -impl Authorizer for PAMAuthorizer { - fn method(&self) -> AuthorizerType { - AuthorizerType::PAM - } - - async fn verify(&self, user: &str, password: &str) -> Result<bool> { - let service = self.provider(); - - let mut auth = pam::Client::with_password(service).unwrap(); - auth.conversation_mut() - .borrow_mut() - .set_credentials(user, password); - Ok(auth.authenticate().is_ok() && auth.open_session().is_ok()) - } } #[derive(Debug, Clone)] |
