From c86aab7847dd62492318e4c0d0a733e5a86a305b Mon Sep 17 00:00:00 2001 From: KunoiSayami <46131041+KunoiSayami@users.noreply.github.com> Date: Thu, 6 May 2021 17:32:17 +0800 Subject: feat: Add configure parser --- src/datastructures.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 57 ++++++++++----------------------- 2 files changed, 103 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/datastructures.rs b/src/datastructures.rs index 7ee01a9..d253fcb 100644 --- a/src/datastructures.rs +++ b/src/datastructures.rs @@ -18,18 +18,69 @@ ** along with this program. If not, see . */ - use sha2::Digest; use anyhow::Result; +use url::form_urlencoded; +use std::borrow::Cow; +use std::path::Path; +use std::fs::read_to_string; +use std::convert::TryFrom; -#[derive(Debug, Clone, Default)] +const DEFAULT_CONFIG_LOCATION: &str = "/etc/cgitrc"; +const DEFAULT_COOKIE_TTL: usize = 1200; +const DEFAULT_DATABASE_LOCATION: &str = "/etc/cgit/auth.db"; + +#[derive(Debug, Clone)] pub struct Config { + pub cookie_ttl: usize, + database: String, +} +impl Default for Config { + fn default() -> Self { + Self { + cookie_ttl: DEFAULT_COOKIE_TTL, + database: DEFAULT_DATABASE_LOCATION.to_string(), + } + } } impl Config { pub fn new() -> Self { - Self {} + Self::load_from_path(DEFAULT_CONFIG_LOCATION) + } + + pub fn load_from_path>(path: P) -> Self { + let file = read_to_string(path).unwrap_or_default(); + let mut cookie_ttl: usize = DEFAULT_COOKIE_TTL; + let mut database: &str = "/etc/cgit/auth.db"; + for line in file.lines() { + + let line = line.trim(); + if !line.contains('=') || !line.starts_with("cgit-simple-auth-") { + continue + } + + let (key, value) = if line.contains('#') { + line.split_once('#').unwrap().0.split_once('=').unwrap() + } else { + line.split_once('=').unwrap() + }; + let key_name = key.split_once("auth-").unwrap().1; + match key_name { + "cookie-ttl" => cookie_ttl = usize::try_from(value).unwrap_or(DEFAULT_COOKIE_TTL), + "database" => database = value, + _ => {} + } + } + Self { + cookie_ttl, + database: database.to_string() + } + } + + pub fn get_database_location(&self) -> &str { + self.database.as_str() } } @@ -72,4 +123,36 @@ impl FormData { } Ok(self.hash.clone()) } +} + +impl From<&[u8]> for FormData { + fn from(input: &[u8]) -> Self { + let fields = form_urlencoded::parse(input); + let mut data = Self::new(); + for f in fields { + match f.0 { + Cow::Borrowed("username") => { + data.set_user(f.1.to_string()); + } + Cow::Borrowed("password") => { + data.set_password(f.1.to_string()); + data.get_password_sha256_cache().unwrap(); + } + _ => {} + } + } + data + } +} + +impl From<&String> for FormData { + fn from(s: &String) -> Self { + Self::from(s.as_bytes()) + } +} + +impl From for FormData { + fn from(s: String) -> Self { + Self::from(&s) + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 81f09e2..95476dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,7 +78,7 @@ async fn verify_login(cfg: &Config, data: &FormData) -> Result { // Processing the `authenticate-basic` called by cgit. fn cmd_authenticate_basic( matches: &ArgMatches, - cfg: Option, + cfg: Config, ) -> Result<()> { unimplemented!() } @@ -86,7 +86,7 @@ fn cmd_authenticate_basic( // Processing the `authenticate-cookie` called by cgit. async fn cmd_authenticate_cookie( matches: &ArgMatches<'_>, - cfg: Option, + cfg: Config, ) -> Result { let cookies = matches.value_of("http-cookie").unwrap_or(""); @@ -94,6 +94,7 @@ async fn cmd_authenticate_cookie( return Ok(false) } + // TODO: use async connection to redis let mut conn = redis::Client::open("redis://127.0.0.1/")?; for cookie in cookies.split(';').map(|x| x.trim()) { @@ -110,11 +111,9 @@ async fn cmd_authenticate_cookie( } -async fn cmd_init(cfg: Option) -> Result<()> { +async fn cmd_init(cfg: Config) -> Result<()> { - - // TODO: read database location from configure file - let mut conn = sqlx::SqliteConnection::connect("sqlite::memory:").await?; + let mut conn = sqlx::SqliteConnection::connect(cfg.get_database_location()).await?; let rows = sqlx::query(r#"SELECT name FROM sqlite_master WHERE type='table' AND name=?"#) .bind("auth_meta") @@ -133,41 +132,21 @@ async fn cmd_init(cfg: Option) -> Result<()> { // Processing the `authenticate-post` called by cgit. async fn cmd_authenticate_post( matches: &ArgMatches<'_>, - cfg: Option, + cfg: Config, ) -> Result<()> { - // Load configurations. - let cfg = cfg.unwrap_or_default(); // Read stdin from upstream. let mut buffer = String::new(); stdin().read_to_string(&mut buffer)?; - let mut data = datastructures::FormData::new(); + let data = datastructures::FormData::from(buffer); // Parsing user posted form. - let fields = form_urlencoded::parse(buffer.as_bytes()); - for f in fields { - match f.0 { - Cow::Borrowed("username") => { - data.set_user(f.1.to_string()) - } - Cow::Borrowed("password") => { - data.set_password(f.1.to_string()) - } - _ => {} - } - } // Authenticated via gogs. if verify_login(&cfg, &data).await.is_ok() { - //let hash = data.hash(); - //let cgitauth = format!("{}:{}", hash, data.nonce); - //let cgitauth_b64 = base64::encode_block(cgitauth.as_bytes()); - //let path = Path::new(&cfg.cache_dir).join(&hash); - //data.to_file(path, true); let cookie = rand_str(COOKIE_LENGTH); - //let redis = redis_async::client::connect(&SocketAddr::from_str("127.0.0.1:5432").unwrap()).await?; - //redis. + // TODO: same here let mut conn = redis::Client::open("redis://127.0.0.1/")?; - conn.set_ex::<_, &str, i32>(format!("cgit_auth_{}", cookie), "1", 600)?; + conn.set_ex::<_, &str, i32>(format!("cgit_auth_{}", cookie), "1", cfg.cookie_ttl)?; let is_secure = matches .value_of("https") @@ -185,9 +164,7 @@ async fn cmd_authenticate_post( println!("Location: {}", location); println!( "Set-Cookie: cgit_auth={}; Domain={}; Max-Age={}; HttpOnly{}", - // TODO: use cookie ttl instead - //cookie, domain, cfg.cookie_ttl, cookie_suffix - cookie, domain, 600, cookie_suffix + cookie, domain, cfg.cookie_ttl, cookie_suffix ); } else { println!("Status: 403 Forbidden"); @@ -201,7 +178,7 @@ async fn cmd_authenticate_post( // Processing the `body` called by cgit. -async fn cmd_body(matches: &ArgMatches<'_>, _cfg: Option) { +async fn cmd_body(matches: &ArgMatches<'_>, _cfg: Config) { let source = include_str!("authentication_page.html"); let handlebars = Handlebars::new(); let meta = Meta { @@ -214,7 +191,7 @@ async fn cmd_body(matches: &ArgMatches<'_>, _cfg: Option) { } // Processing the `body` called by cron. -fn cmd_expire(_matches: &ArgMatches, cfg: Option) { +fn cmd_expire(_matches: &ArgMatches, cfg: Config) { unimplemented!(); } @@ -222,21 +199,21 @@ fn cmd_expire(_matches: &ArgMatches, cfg: Option) { async fn async_main(arg_matches: ArgMatches<'_>, cfg: Config) -> Result{ match arg_matches.subcommand() { ("authenticate-cookie", Some(matches)) => { - if cmd_authenticate_cookie(matches, Some(cfg)).await.is_ok() { + if cmd_authenticate_cookie(matches, cfg).await.is_ok() { return Ok(1) } } ("authenticate-post", Some(matches)) => { - cmd_authenticate_post(matches, Some(cfg)).await.unwrap(); + cmd_authenticate_post(matches, cfg).await.unwrap(); } ("body", Some(matches)) => { - cmd_body(matches, Some(cfg)).await; + cmd_body(matches, cfg).await; } ("expire", Some(matches)) => { - cmd_expire(matches, Some(cfg)); + cmd_expire(matches, cfg); } ("init", Some(matches)) => { - cmd_init(Some(cfg)).await?; + cmd_init(cfg).await?; } _ => {} } -- cgit v1.3.1