diff options
| author | KunoiSayami <[email protected]> | 2021-05-10 16:09:19 +0800 |
|---|---|---|
| committer | KunoiSayami <[email protected]> | 2021-05-10 16:09:19 +0800 |
| commit | 309a78d246ff175eaf12f6a7d4e9d70845b9edda (patch) | |
| tree | 6665551380832e1509f0715dc055d94def90e6a2 | |
| parent | c5ed7b895caad6dc4e6d3398fbe3c154a102959c (diff) | |
feat: Add reset database subcommandv0.2.1
* fix: Fix CI build failure
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | src/database.rs | 8 | ||||
| -rw-r--r-- | src/datastructures.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 163 |
6 files changed, 121 insertions, 66 deletions
@@ -377,7 +377,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cgit-simple-authentication" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "argon2", @@ -1,6 +1,6 @@ [package] name = "cgit-simple-authentication" -version = "0.2.0" +version = "0.2.1" authors = ["KunoiSayami <[email protected]>"] edition = "2018" @@ -2,6 +2,10 @@ This project is currently developing, minor version may incompatible with previous minor version. +## Notice + +Upgrade from v0.1.x to v0.2.x should reset database due to password storage method changed. + ## Source Most idea from: https://github.com/varphone/cgit-gogs-auth-filter diff --git a/src/database.rs b/src/database.rs index 0998b2e..a9af02b 100644 --- a/src/database.rs +++ b/src/database.rs @@ -36,6 +36,14 @@ pub mod v1 { INSERT INTO "auth_meta" VALUES ('version', '1'); "#; + pub const DROP_TABLES: &str = r#" + + DROP TABLE "accounts"; + + DROP TABLE "auth_meta"; + + "#; + pub const VERSION: &str = "1"; } pub use v1 as current; diff --git a/src/datastructures.rs b/src/datastructures.rs index bb91cfb..319446d 100644 --- a/src/datastructures.rs +++ b/src/datastructures.rs @@ -35,9 +35,9 @@ use rand_core::OsRng; const DEFAULT_CONFIG_LOCATION: &str = "/etc/cgitrc"; const DEFAULT_COOKIE_TTL: u64 = 1200; const DEFAULT_DATABASE_LOCATION: &str = "/etc/cgit/auth.db"; -pub const CACHE_DIR: &str = "/var/cache/cgit"; +//pub const CACHE_DIR: &str = "/var/cache/cgit"; pub type RandIntType = u32; -pub const MINIMUM_SECRET_LENGTH: usize = 8; +//pub const MINIMUM_SECRET_LENGTH: usize = 8; pub fn get_current_timestamp() -> u64 { let start = std::time::SystemTime::now(); @@ -100,7 +100,7 @@ impl Config { let mut cookie_ttl: u64 = DEFAULT_COOKIE_TTL; let mut database: &str = "/etc/cgit/auth.db"; let mut bypass_root: bool = false; - let mut secret: &str = ""; + //let mut secret: &str = ""; for line in file.lines() { let line = line.trim(); if !line.contains('=') || !line.starts_with("cgit-simple-auth-") { @@ -117,7 +117,7 @@ impl Config { "cookie-ttl" => cookie_ttl = value.parse().unwrap_or(DEFAULT_COOKIE_TTL), "database" => database = value, "bypass-root" => bypass_root = value.to_lowercase().eq("true"), - "secret" => secret = value, + //"secret" => secret = value, _ => {} } } diff --git a/src/main.rs b/src/main.rs index 0f5fd48..76c7fa3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![feature(array_methods)] /* ** Copyright (C) 2021 KunoiSayami ** @@ -31,7 +30,7 @@ use log4rs::config::{Appender, Root}; use log4rs::encode::pattern::PatternEncoder; use redis::AsyncCommands; use serde::Serialize; -use sqlx::Connection; +use sqlx::{Connection, ConnectOptions, SqliteConnection}; use std::env; use std::io::{stdin, Read}; use std::result::Result::Ok; @@ -39,6 +38,7 @@ use tokio_stream::StreamExt as _; use argon2::{ password_hash::{PasswordHash}, }; +use std::str::FromStr; const COOKIE_LENGTH: usize = 45; @@ -100,15 +100,10 @@ async fn cmd_init(cfg: Config) -> Result<()> { } async fn verify_login(cfg: &Config, data: &FormData) -> Result<bool> { - // TODO: use timestamp to mark file diff - // or copy in init process - let database_file_name = std::path::Path::new(datastructures::CACHE_DIR).join( - std::path::Path::new(cfg.get_database_location()) - .file_name() - .unwrap(), - ); - std::fs::copy(cfg.get_database_location(), database_file_name.clone())?; - let mut conn = sqlx::SqliteConnection::connect(database_file_name.to_str().unwrap()).await?; + let mut conn = sqlx::sqlite::SqliteConnectOptions::from_str(cfg.get_database_location())? + .read_only(true) + .log_statements(log::LevelFilter::Trace) + .connect().await?; let (passwd_hash,) = sqlx::query_as::<_, (String, )>(r#"SELECT "password" FROM "accounts" WHERE "user" = ?"#) .bind(data.get_user()) .fetch_one(&mut conn) @@ -121,6 +116,7 @@ async fn verify_login(cfg: &Config, data: &FormData) -> Result<bool> { async fn cmd_authenticate_post(matches: &ArgMatches<'_>, cfg: Config) -> Result<()> { // Read stdin from upstream. let mut buffer = String::new(); + // TODO: override it that can test function from cargo test stdin().read_to_string(&mut buffer)?; log::debug!("{}", buffer); let data = datastructures::FormData::from(buffer); @@ -177,7 +173,7 @@ pub struct Meta<'a> { // Processing the `body` called by cgit. -async fn cmd_body(matches: &ArgMatches<'_>, cfg: Config) { +async fn cmd_body(matches: &ArgMatches<'_>, _cfg: Config) { let source = include_str!("authentication_page.html"); let handlebars = Handlebars::new(); let meta = Meta { @@ -197,8 +193,8 @@ async fn cmd_add_user(matches: &ArgMatches<'_>, cfg: Config) -> Result<()> { return Err(anyhow::Error::msg("Invalid user or password")); } - if user.len() > 20 { - return Err(anyhow::Error::msg("Username length should less than 20")) + if user.len() >= 20 { + return Err(anyhow::Error::msg("Username length should less than 21")) } let mut conn = sqlx::SqliteConnection::connect(cfg.get_database_location()).await?; @@ -274,6 +270,26 @@ async fn cmd_delete_user(matches: &ArgMatches<'_>, cfg: Config) -> Result<()> { Ok(()) } +async fn cmd_reset_database(matches: &ArgMatches<'_>, cfg: Config) -> Result<()> { + if !matches.is_present("confirm") { + return Err(anyhow::Error::msg("Please add --confirm argument to process reset")) + } + + let mut conn = SqliteConnection::connect(cfg.get_database_location()).await?; + + sqlx::query(database::current::DROP_TABLES) + .execute(&mut conn) + .await?; + + sqlx::query(database::current::CREATE_TABLES) + .execute(&mut conn) + .await?; + + println!("Reset database successfully"); + + Ok(()) +} + async fn async_main(arg_matches: ArgMatches<'_>, cfg: Config) -> Result<i32> { match arg_matches.subcommand() { ("authenticate-cookie", Some(matches)) => { @@ -302,44 +318,15 @@ async fn async_main(arg_matches: ArgMatches<'_>, cfg: Config) -> Result<i32> { ("deluser", Some(matches)) => { cmd_delete_user(matches, cfg).await?; } + ("reset", Some(matches)) => { + cmd_reset_database(matches, cfg).await?; + } _ => {} } Ok(0) } -fn main() -> Result<()> { - let logfile = FileAppender::builder() - .encoder(Box::new(PatternEncoder::new( - "{d(%Y-%m-%d %H:%M:%S)}- {h({l})} - {m}{n}", - ))) - .build(option_env!("RUST_LOG_FILE").unwrap_or("/tmp/auth.log"))?; - - let config = log4rs::Config::builder() - .appender(Appender::builder().build("logfile", Box::new(logfile))) - .logger(log4rs::config::Logger::builder().build("sqlx::query", log::LevelFilter::Warn)) - .logger( - log4rs::config::Logger::builder().build("handlebars::render", log::LevelFilter::Warn), - ) - .logger( - log4rs::config::Logger::builder().build("handlebars::context", log::LevelFilter::Warn), - ) - .build( - Root::builder() - .appender("logfile") - .build(log::LevelFilter::Debug), - )?; - - log4rs::init_config(config)?; - //simple_logging::log_to_file("/tmp/auth.log", log::LevelFilter::Debug)?; - - log::debug!( - "{}", - env::args() - .enumerate() - .map(|(nth, arg)| format!("[{}]={}", nth, arg)) - .collect::<Vec<String>>() - .join(" ") - ); +fn process_arguments(arguments: Option<Vec<&str>>) -> Result<()> { // Sub-arguments for each command, see cgi defines. let sub_args = &[ @@ -356,7 +343,7 @@ fn main() -> Result<()> { Arg::with_name("login-url").required(true), ]; - let matches = App::new("Simple Authentication Filter for cgit") + let app = App::new("Simple Authentication Filter for cgit") .version(env!("CARGO_PKG_VERSION")) .subcommand( SubCommand::with_name("authenticate-cookie") @@ -386,7 +373,18 @@ fn main() -> Result<()> { .about("Delete user from database") .arg(Arg::with_name("user").required(true)) ) - .get_matches(); + .subcommand( + SubCommand::with_name("reset") + .about("Reset database") + .arg(Arg::with_name("confirm").long("confirm")) + ); + + let matches = if let Some(args) = arguments { + app.get_matches_from(args) + } else { + app.get_matches() + }; + // Load filter configurations let cfg = Config::new(); @@ -403,19 +401,54 @@ fn main() -> Result<()> { Ok(()) } -mod test { - const PASSWORD: &str = "hunter2"; - const ARGON2_HASH: &str = "$argon2id$v=19$m=4096,t=3,p=1$szYDnoQSVPmXq+RD2LneBw$fRETH//iCQuIX+SgjYPdZ9iIbM8gEy9fBjTJ/KFFJNM"; - use argon2::{ - password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, - Argon2 - }; - use rand_core::OsRng; +fn main() -> Result<()> { + let logfile = FileAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "{d(%Y-%m-%d %H:%M:%S)}- {h({l})} - {m}{n}", + ))) + .build(option_env!("RUST_LOG_FILE").unwrap_or("/tmp/auth.log"))?; + let config = log4rs::Config::builder() + .appender(Appender::builder().build("logfile", Box::new(logfile))) + //.logger(log4rs::config::Logger::builder().build("sqlx::query", log::LevelFilter::Warn)) + .logger( + log4rs::config::Logger::builder().build("handlebars::render", log::LevelFilter::Warn), + ) + .logger( + log4rs::config::Logger::builder().build("handlebars::context", log::LevelFilter::Warn), + ) + .build( + Root::builder() + .appender("logfile") + .build(log::LevelFilter::Debug), + )?; + + log4rs::init_config(config)?; + log::debug!( + "{}", + env::args() + .enumerate() + .map(|(nth, arg)| format!("[{}]={}", nth, arg)) + .collect::<Vec<String>>() + .join(" ") + ); + + process_arguments(None)?; + + Ok(()) +} + +mod test { + use crate::process_arguments; #[test] fn test_argon2() { - let passwd = PASSWORD.as_bytes(); + use argon2::{ + password_hash::{PasswordHasher, SaltString}, + Argon2 + }; + use rand_core::OsRng; + let passwd = b"hunter2"; let salt = SaltString::generate(&mut OsRng); let argon2 = Argon2::default(); @@ -426,10 +459,20 @@ mod test { #[test] fn test_argon2_verify() { - let passwd = PASSWORD.as_bytes(); - let parsed_hash = PasswordHash::new(ARGON2_HASH).unwrap(); + use argon2::{ + password_hash::{PasswordHash, PasswordVerifier}, + Argon2 + }; + let passwd = b"hunter2"; + let parsed_hash = PasswordHash::new("$argon2id$v=19$m=4096,t=3,p=1$szYDnoQSVPmXq+RD2LneBw$fRETH//iCQuIX+SgjYPdZ9iIbM8gEy9fBjTJ/KFFJNM").unwrap(); let argon2 = Argon2::default(); assert!(argon2.verify_password(passwd, &parsed_hash).is_ok()) } + + #[cfg(unix)] + #[allow(dead_code)] + fn test_auth_post() { + process_arguments(Some(vec!["cgit-simple-authentication", "authenticate-post", "", "POST", "p=login", "https://git.example.com/?p=login", "/", "git.example.com", "", "", "login", "/?p=login", "/?p=login"])).unwrap(); + } } |
