aboutsummaryrefslogtreecommitdiff
path: root/src/datastructures.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/datastructures.rs')
-rw-r--r--src/datastructures.rs160
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)]