diff options
author | KunoiSayami <[email protected]> | 2021-05-08 01:22:08 +0800 |
---|---|---|
committer | KunoiSayami <[email protected]> | 2021-05-08 01:22:08 +0800 |
commit | 86e4c76dabb20b7f489abcb1f1c3cdb11a494b91 (patch) | |
tree | 8369ddbd694637a61fc337c71e89d61e62bc8286 | |
parent | 3a8881fbaab4d18b865a44198db9c195041cbff2 (diff) |
fix: Fix authenticate-cookie not working properlyv0.1.2
* feat: Add adduser subcommand
-rw-r--r-- | .github/workflows/build-cross.yml | 36 | ||||
-rw-r--r-- | .github/workflows/build.yml | 59 | ||||
-rw-r--r-- | Cargo.lock | 35 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | src/database.rs | 3 | ||||
-rw-r--r-- | src/datastructures.rs | 18 | ||||
-rw-r--r-- | src/main.rs | 111 |
7 files changed, 226 insertions, 43 deletions
diff --git a/.github/workflows/build-cross.yml b/.github/workflows/build-cross.yml new file mode 100644 index 0000000..68f08fd --- /dev/null +++ b/.github/workflows/build-cross.yml @@ -0,0 +1,36 @@ +name: Build cross binary + +on: + push: + tags: + - v** + pull_request: + +jobs: + build_aarch64: + runs-on: ubuntu-latest + steps: + - uses: actions/[email protected] + - uses: actions-rs/[email protected] + with: + toolchain: stable + target: aarch64-unknown-linux-musl + override: true + - name: Build aarch 64 binary + uses: actions-rs/[email protected] + with: + use-cross: true + command: build + args: --target aarch64-unknown-linux-musl --release + - run: mv target/aarch64-unknown-linux-musl/release/cgit-simple-authentication target/aarch64-unknown-linux-musl/release/cgit-simple-authentication_linux_aarch64 + - uses: actions/[email protected] + with: + name: aarch64-artifact + path: target/aarch64-unknown-linux-musl/release/cgit-simple-authentication_linux_aarch64 + - name: Release + uses: softprops/[email protected] + if: startsWith(github.ref, 'refs/tags/') + with: + files: target/aarch64-unknown-linux-musl/release/cgit-simple-authentication_linux_aarch64 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9e7f700 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,59 @@ +name: Build binary + +on: + push: + tags: + - v** + pull_request: + +jobs: + build: + strategy: + fail-fast: true + matrix: + job: + - { os: macos-latest } + - { os: ubuntu-latest } + - { os: windows-latest } + + + name: Build + runs-on: ${{ matrix.job.os }} + steps: + - uses: actions/[email protected] + - uses: actions-rs/[email protected] + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/[email protected] + with: + command: build + args: --release + - name: Rename binary + id: rename + shell: bash + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + BIN='cgit-simple-authentication_linux_amd64' + mv target/release/cgit-simple-authentication target/release/$BIN + elif [ "$RUNNER_OS" == "macOS" ]; then + BIN='cgit-simple-authentication_darwin_amd64' + mv target/release/cgit-simple-authentication target/release/$BIN + else + BIN='cgit-simple-authentication_windows_amd64.exe' + mv target/release/cgit-simple-authentication.exe target/release/$BIN + fi + echo "::set-output name=bin::target/release/$BIN" + - uses: actions/[email protected] + with: + name: artifact + path: | + target/release/cgit-simple-authentication_* + - name: Release + uses: softprops/[email protected] + if: startsWith(github.ref, 'refs/tags/') + with: + files: ${{ steps.rename.outputs.bin }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file @@ -344,7 +344,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cgit-simple-authentication" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "base64", @@ -358,6 +358,7 @@ dependencies = [ "serde_derive", "serde_json", "sha2", + "simple-logging", "sqlx", "tokio 1.5.0", "toml", @@ -1177,7 +1178,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.8", "smallvec", "winapi 0.3.9", ] @@ -1420,6 +1421,12 @@ dependencies = [ [[package]] name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" @@ -1571,6 +1578,17 @@ dependencies = [ ] [[package]] +name = "simple-logging" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542" +dependencies = [ + "lazy_static", + "log", + "thread-id", +] + +[[package]] name = "slab" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1738,7 +1756,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand 0.8.3", - "redox_syscall", + "redox_syscall 0.2.8", "remove_dir_all", "winapi 0.3.9", ] @@ -1782,6 +1800,17 @@ dependencies = [ ] [[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi 0.3.9", +] + +[[package]] name = "tinyvec" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,11 +1,11 @@ [package] name = "cgit-simple-authentication" -version = "0.1.1" +version = "0.1.2" authors = ["KunoiSayami <[email protected]>"] edition = "2018" [dependencies] -log = { version = "0.4", features = ["max_level_trace", "release_max_level_debug"] } +log = { version = "0.4", features = ["max_level_trace", "release_max_level_trace"] } env_logger = "0.8" tokio = { version = "1", features = ["full"] } serde_json = "1" @@ -20,4 +20,5 @@ handlebars = "3.4" url = "2.1" redis = { version = "0.17.0", features = ["tokio-comp"] } sha2 = "0.9.3" -base64 = "0.13.0"
\ No newline at end of file +base64 = "0.13.0" +simple-logging = "2.0.2"
\ No newline at end of file diff --git a/src/database.rs b/src/database.rs index 1a1dedf..0998b2e 100644 --- a/src/database.rs +++ b/src/database.rs @@ -37,7 +37,6 @@ pub mod v1 { "#; pub const VERSION: &str = "1"; - } -pub use v1::VERSION; pub use v1 as current; +pub use v1::VERSION; diff --git a/src/datastructures.rs b/src/datastructures.rs index 6897bd3..b920eb4 100644 --- a/src/datastructures.rs +++ b/src/datastructures.rs @@ -28,6 +28,7 @@ use std::fs::read_to_string; 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"; #[derive(Debug, Clone)] pub struct Config { @@ -97,6 +98,12 @@ impl FormData { Self { ..Default::default()} } + pub fn get_string_sha256_value(s: &str) -> Result<String> { + let mut hasher = sha2::Sha256::new(); + hasher.update(s.as_bytes()); + Ok(format!("{:x}", hasher.finalize())) + } + pub fn set_password(&mut self, password: String) { self.password = password; self.hash = Default::default(); @@ -111,18 +118,21 @@ impl FormData { } pub fn get_password_sha256(&self) -> Result<String> { - let mut hasher = sha2::Sha256::new(); - hasher.update(self.password.as_bytes()); - Ok(format!("{:x}", hasher.finalize())) + Self::get_string_sha256_value(&self.password) } #[allow(dead_code)] pub fn get_password_sha256_cache(&mut self) -> Result<String> { - if self.hash.len() == 0 { + if self.hash.is_empty() { self.hash = self.get_password_sha256()?; } Ok(self.hash.clone()) } + + #[allow(dead_code)] + pub fn get_sha256_without_calc(&self) -> &String { + &self.hash + } } impl From<&[u8]> for FormData { diff --git a/src/main.rs b/src/main.rs index 9ef6c2e..0875993 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,9 +31,11 @@ use handlebars::Handlebars; use sqlx::Connection; use crate::datastructures::{Config, FormData}; use redis::AsyncCommands; +use std::result::Result::Ok; const COOKIE_LENGTH: usize = 45; + fn get_current_timestamp() -> u64 { let start = std::time::SystemTime::now(); let since_the_epoch = start @@ -71,22 +73,10 @@ struct Meta<'a> { } -async fn verify_login(cfg: &Config, data: &FormData) -> Result<bool> { - let mut conn = sqlx::SqliteConnection::connect(cfg.get_database_location()).await?; - let password_sha = data.get_password_sha256()?; - let ret = sqlx::query(r#"SELECT 1 FROM "accounts" WHERE "user" = ? AND "password" = ? "#) - .bind(data.get_user()) - .bind(password_sha) - .fetch_all(&mut conn) - .await?; - Ok(ret.len() > 0) -} - - // Processing the `authenticate-basic` called by cgit. fn cmd_authenticate_basic( - matches: &ArgMatches, - cfg: Config, + _matches: &ArgMatches, + _cfg: Config, ) -> Result<()> { unimplemented!() } @@ -109,8 +99,8 @@ async fn cmd_authenticate_cookie( for cookie in cookies.split(';').map(|x| x.trim()) { let (key, value) = cookie.split_once('=').unwrap(); if key.eq("cgit_auth") { - let value = base64::decode(value).unwrap_or(vec![]); - let value = String::from_utf8(value).unwrap_or("".to_string()); + let value = base64::decode(value).unwrap_or_default(); + let value = std::str::from_utf8(&value).unwrap_or(""); if !value.contains(';') { break @@ -162,6 +152,22 @@ async fn cmd_init(cfg: Config) -> Result<()> { Ok(()) } +async fn verify_login(cfg: &Config, data: &FormData) -> Result<bool> { + 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 password_sha = data.get_password_sha256()?; + log::debug!("password: {}", password_sha); + let ret = sqlx::query(r#"SELECT 1 FROM "accounts" WHERE "user" = ? AND "password" = ? "#) + .bind(data.get_user()) + .bind(password_sha) + .fetch_all(&mut conn) + .await?; + Ok(!ret.is_empty()) +} + + // Processing the `authenticate-post` called by cgit. async fn cmd_authenticate_post( matches: &ArgMatches<'_>, @@ -170,23 +176,29 @@ async fn cmd_authenticate_post( // Read stdin from upstream. let mut buffer = String::new(); stdin().read_to_string(&mut buffer)?; + log::debug!("{}", buffer); let data = datastructures::FormData::from(buffer); // Parsing user posted form. - // Authenticated via gogs. - if verify_login(&cfg, &data).await.is_ok() { + let ret = verify_login(&cfg, &data).await; + + if let Err(ref e) = ret { + log::error!("{:?}", e) + } + + if ret.unwrap() { let key = format!("{}_{}", get_current_timestamp(), rand_int()); let value = rand_str(COOKIE_LENGTH); let redis_conn = redis::Client::open("redis://127.0.0.1/")?; let mut conn = redis_conn.get_async_connection().await?; - conn.set_ex::<_, _, i32>(format!("cgit_auth_{}", key), &value, cfg.cookie_ttl as usize).await?; + conn.set_ex::<_, _, String>(format!("cgit_auth_{}", key), &value, cfg.cookie_ttl as usize).await?; let cookie_value = base64::encode(format!("{};{}", key, value)); let is_secure = matches .value_of("https") - .map_or(false, |x| matches!(x, "yes" | "on" | "1")); + .map_or(false, | x | matches!(x, "yes" | "on" | "1")); let domain = matches.value_of("http-host").unwrap_or("*"); let location = matches .value_of("current-url") @@ -195,7 +207,7 @@ async fn cmd_authenticate_post( .next() .unwrap(); let cookie_suffix = if is_secure { "; secure" } else { "" }; - println!("Status: 302 Redirect"); + println!("Status: 302 Found"); println!("Cache-Control: no-cache, no-store"); println!("Location: {}", location); println!( @@ -227,35 +239,65 @@ async fn cmd_body(matches: &ArgMatches<'_>, _cfg: Config) { } +async fn cmd_add_user(matches: &ArgMatches<'_>, cfg: Config) -> Result<()>{ + let user = matches.value_of("user").unwrap_or(""); + let passwd = matches.value_of("password").unwrap_or("").to_string(); + if user.is_empty() || passwd.is_empty() { + return Err(anyhow::Error::msg("Invalid user or password")) + } + let mut conn = sqlx::SqliteConnection::connect(cfg.get_database_location()).await?; + + let items = sqlx::query(r#"SELECT 1 FROM "accounts" WHERE "user" = ? "#) + .bind(user) + .fetch_all(&mut conn) + .await?; + + if ! items.is_empty() { + return Err(anyhow::Error::msg("User already exists!")) + } + + sqlx::query(r#"INSERT INTO "accounts" ("user", "password") VALUES (?, ?) "#) + .bind(user) + .bind(FormData::get_string_sha256_value(&passwd)?) + .execute(&mut conn) + .await?; + println!("Insert {} to database", user); + Ok(()) +} + async fn async_main(arg_matches: ArgMatches<'_>, cfg: Config) -> Result<i32>{ match arg_matches.subcommand() { ("authenticate-cookie", Some(matches)) => { - if cmd_authenticate_cookie(matches, cfg).await.is_ok() { - return Ok(1) + if let Ok(should_pass) = cmd_authenticate_cookie(matches, cfg).await { + if should_pass { + return Ok(1) + } } } ("authenticate-post", Some(matches)) => { - cmd_authenticate_post(matches, cfg).await.unwrap(); + cmd_authenticate_post(matches, cfg).await?; } ("body", Some(matches)) => { cmd_body(matches, cfg).await; } - ("init", Some(matches)) => { + ("init", Some(_matches)) => { cmd_init(cfg).await?; } + ("adduser", Some(matches)) => { + cmd_add_user(matches, cfg).await?; + } _ => {} } + log::debug!("exit"); Ok(0) } - fn main() -> Result<()>{ - env_logger::init(); - // Prints each argument on a separate line - for (nth, argument) in env::args().enumerate() { - log::debug!("[{}]={}", nth, argument); - } + simple_logging::log_to_file("/tmp/auth.log", log::LevelFilter::Debug)?; + + log::debug!("{}", env::args().collect::<Vec<String>>() + .join(" ")); // Sub-arguments for each command, see cgi defines. let sub_args = &[ @@ -290,6 +332,13 @@ fn main() -> Result<()>{ .args(sub_args), ) .subcommand(SubCommand::with_name("init").about("Init sqlite database")) + .subcommand( + SubCommand::with_name("adduser") + .about("Add user to database") + .arg(Arg::with_name("user").required(true)) + .arg(Arg::with_name("password").required(true)), + + ) .get_matches(); // Load filter configurations |