aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build-cross.yml36
-rw-r--r--.github/workflows/build.yml59
-rw-r--r--Cargo.lock35
-rw-r--r--Cargo.toml7
-rw-r--r--src/database.rs3
-rw-r--r--src/datastructures.rs18
-rw-r--r--src/main.rs111
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
diff --git a/Cargo.lock b/Cargo.lock
index 1a5e885..c9637a7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 167c42e..4bb3f9e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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