diff --git a/Cargo.lock b/Cargo.lock index 8144eb5..78d2acc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,6 +245,55 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.72" @@ -290,6 +339,7 @@ version = "0.1.0" dependencies = [ "actix-web", "anyhow", + "clap", "env_logger", "libinjection", "log", @@ -415,6 +465,53 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "convert_case" version = "0.4.0" @@ -667,6 +764,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.2" @@ -1344,6 +1447,12 @@ dependencies = [ "sqlite3-src", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -1527,6 +1636,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 33737fb..fadb81f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ sha2 = "0.10.2" pbkdf2 = { version = "0.12", features = ["simple"] } env_logger = "0.10" log = "0.4" +clap = { version = "4.3.21", features = ["derive"] } diff --git a/README.md b/README.md index 23dde6b..994875c 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,9 @@ Authenticates using an existing account. ## Arguments -| Argument | Description | -|----------------------------|-----------------------------------------------------------------------------------------| -| `-D / --daemon` | Starts the server in daemon mode, to await api requests, etc. | -| `-d / --delete ` | Deletes the account associated with the specified username. | -| `-w / --dir ` | Specifies the working dir that will be used. This dir contains stuff like the database. | +| Argument | Description | +|-----------------------------|-----------------------------------------------------------------------------------------| +| `-D / --daemon ` | Starts the server in daemon mode, to await api requests on the specified port, etc. | +| `-d / --delete ` | Deletes the account(s) associated with the specified username(s). | +| `-l / --list` | Lists all registered accounts | +| `-w / --dir ` | Specifies the working dir that will be used. This dir contains stuff like the database. | diff --git a/src/backend.rs b/src/backend.rs index 8514428..351718b 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -12,14 +12,6 @@ pub struct Backend { db: sqlite::Connection, } -#[derive(Debug)] -pub struct Account { - id: i64, - username: String, - salt: String, - password: String, -} - fn is_sql_injection(string: &String) -> bool { match sqli(string) { Some((is_injection, _)) => is_injection, @@ -42,21 +34,6 @@ impl Backend { Ok(false) } - fn get_user(&mut self, username: &String) -> Result { - let mut statement = self.db.prepare(format!( - "SELECT * FROM Accounts WHERE username='{}';", - username - ))?; - statement.next()?; - - Ok(Account { - id: statement.read::("id").unwrap(), - username: statement.read::("username").unwrap(), - salt: statement.read::("salt").unwrap(), - password: statement.read::("password").unwrap(), - }) - } - pub fn register(&mut self, username: &String, password: &String) -> Result { if is_sql_injection(username) { warn!( @@ -99,8 +76,15 @@ impl Backend { return Ok(Response::Authenticate(ResponseAuthenticate::UserNotFound)); } - let account = self.get_user(username)?; - let hash = PasswordHash::new(&account.password) + let mut statement = self.db.prepare(format!( + "SELECT * FROM Accounts WHERE username='{}';", + username + ))?; + statement.next()?; + + let raw_hash = &statement.read::("password").unwrap(); + + let hash = PasswordHash::new(raw_hash) .map_err(|_| anyhow::Error::msg("Failed to parse the password hash"))?; match Pbkdf2.verify_password(password.as_bytes(), &hash) { @@ -120,8 +104,17 @@ impl Backend { return Ok(Response::Delete(ResponseDelete::Rejected)); } - println!(" Deletion Request: (Username: '{}')", username); - println!(" -> Accepted"); + if !self.user_exists(username)? { + warn!("Deletion failed, user `{}` not found", username); + return Ok(Response::Delete(ResponseDelete::UserNotFound)); + } + + self.db.execute(format!( + "DELETE FROM Accounts WHERE username='{}';", + username + ))?; + + info!("Deleted `{}`)", username); Ok(Response::Delete(ResponseDelete::Success)) } diff --git a/src/main.rs b/src/main.rs index 94bd0f0..cd83a4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,17 +3,58 @@ mod backend; mod call; use anyhow::Result; +use call::Call; +use clap::Parser; use log::info; +use std::path::{Path, PathBuf}; +use tokio::sync::oneshot; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Sets the operation directory + #[arg(short = 'w', long, value_name = "PATH")] + dir: Option, + + /// Start in daemon / server mode + #[arg(short = 'D', long)] + daemon: bool, + + /// Delete one or multiple account(s) + #[arg(short, long, use_value_delimiter = true, value_delimiter = ',')] + deletions: Option>, +} #[tokio::main] async fn main() -> Result<()> { - env_logger::init(); + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + let args = Args::parse(); + + if let Some(dir) = args.dir { + std::env::set_current_dir(dir)?; + } + info!("Starting BaseAuth server v0.1"); - let backend = backend::Backend::new(&std::path::Path::new("db.sqlite"))?; - + let backend = backend::Backend::new(&Path::new("db.sqlite"))?; let tx = call::start_worker(backend).await?; - api::start_worker(8080, tx).await?; - loop {} + if let Some(d) = &args.deletions { + for username in d.iter() { + let (oneshot_tx, oneshot_rx) = oneshot::channel(); + tx.send((Call::Delete(username.to_string()), oneshot_tx)) + .await?; + + let _ = oneshot_rx.await??; + } + } + + if args.daemon { + api::start_worker(8080, tx).await?; + loop {} + } else if args.deletions == None { + println!("Nothing to do. run `baseauth --help` for a list of commands"); + } + + Ok(()) }