aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKunoiSayami <[email protected]>2021-05-10 16:09:19 +0800
committerKunoiSayami <[email protected]>2021-05-10 16:09:19 +0800
commit309a78d246ff175eaf12f6a7d4e9d70845b9edda (patch)
tree6665551380832e1509f0715dc055d94def90e6a2
parentc5ed7b895caad6dc4e6d3398fbe3c154a102959c (diff)
feat: Add reset database subcommandv0.2.1
* fix: Fix CI build failure
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.md4
-rw-r--r--src/database.rs8
-rw-r--r--src/datastructures.rs8
-rw-r--r--src/main.rs163
6 files changed, 121 insertions, 66 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ceed4a1..f878d36 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -377,7 +377,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cgit-simple-authentication"
-version = "0.2.0"
+version = "0.2.1"
dependencies = [
"anyhow",
"argon2",
diff --git a/Cargo.toml b/Cargo.toml
index f16bad0..4fbfe80 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cgit-simple-authentication"
-version = "0.2.0"
+version = "0.2.1"
authors = ["KunoiSayami <[email protected]>"]
edition = "2018"
diff --git a/README.md b/README.md
index 1b50fb3..77a9ce9 100644
--- a/README.md
+++ b/README.md
@@ -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();
+ }
}