diff --git a/Cargo.lock b/Cargo.lock index 5ca73c0..198259f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,6 +196,19 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +[[package]] +name = "bcrypt" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df288bec72232f78c1ec5fe4e8f1d108aa0265476e93097593c803c8c02062a" +dependencies = [ + "base64 0.21.0", + "blowfish", + "getrandom", + "subtle", + "zeroize", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -220,6 +233,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "bumpalo" version = "3.11.1" @@ -271,6 +294,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "4.0.25" @@ -812,6 +845,15 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1034,6 +1076,7 @@ dependencies = [ "axum", "axum-auth", "axum-macros", + "bcrypt", "bytes", "chrono", "clap", @@ -2251,3 +2294,9 @@ name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index 8eb738b..a92b21c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,4 @@ jwt = "0.16.0" hmac = "0.12.1" sha2 = "0.10.6" rand = "0.8.5" +bcrypt = "0.14.0" diff --git a/src/api/auth.rs b/src/api/auth.rs index dc85b51..7d9a51c 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -13,6 +13,7 @@ use sha2::Sha256; use rand::Rng; use crate::{dto::scope::Scope, app_state::AppState}; +use crate::database::Database; #[derive(Deserialize, Debug)] pub struct TokenAuthRequest { @@ -158,7 +159,18 @@ pub async fn auth_basic_get(basic_auth: Option, state: State, last_repo: Option) -> sqlx::Result>; + + + /// User stuff + async fn create_user(&self, username: String, email: String, password_hash: String, password_salt: String) -> sqlx::Result; + async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result; } #[async_trait] @@ -251,4 +256,26 @@ impl Database for Pool { Ok(repos) } + + async fn create_user(&self, username: String, email: String, password_hash: String, password_salt: String) -> sqlx::Result { + let username = username.to_lowercase(); + let email = email.to_lowercase(); + sqlx::query("INSERT INTO users (username, email, password_hash, password_salt) VALUES (?, ?, ?, ?)") + .bind(username.clone()) + .bind(email.clone()) + .bind(password_hash) + .bind(password_salt) + .execute(self).await?; + + Ok(User::new(username, email)) + } + + async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result { + let username = username.to_lowercase(); + let row: (String, ) = sqlx::query_as("SELECT password_hash FROM users WHERE username = ?") + .bind(username) + .fetch_one(self).await?; + + Ok(bcrypt::verify(password, &row.0)?) + } } \ No newline at end of file diff --git a/src/database/schemas/schema.sql b/src/database/schemas/schema.sql index dc275a2..c60f5a4 100644 --- a/src/database/schemas/schema.sql +++ b/src/database/schemas/schema.sql @@ -8,11 +8,6 @@ CREATE TABLE IF NOT EXISTS image_manifests ( content TEXT NOT NULL ); -CREATE TABLE IF NOT EXISTS layer_blobs ( - digest TEXT NOT NULL PRIMARY KEY, - blob BYTEA NOT NULL -); - CREATE TABLE IF NOT EXISTS image_tags ( name TEXT NOT NULL, repository TEXT NOT NULL, @@ -26,3 +21,10 @@ CREATE TABLE IF NOT EXISTS manifest_layers ( layer_digest TEXT NOT NULL, PRIMARY KEY (manifest, layer_digest) ); + +CREATE TABLE IF NOT EXISTS users ( + username TEXT NOT NULL UNIQUE PRIMARY KEY, + email TEXT NOT NULL, + password_hash TEXT NOT NULL, + password_salt TEXT NOT NULL +); \ No newline at end of file diff --git a/src/dto/mod.rs b/src/dto/mod.rs index d12df3d..323cefe 100644 --- a/src/dto/mod.rs +++ b/src/dto/mod.rs @@ -3,6 +3,7 @@ use chrono::{DateTime, Utc}; pub mod manifest; pub mod digest; pub mod scope; +pub mod user; #[derive(Debug)] pub struct Tag { diff --git a/src/dto/user.rs b/src/dto/user.rs new file mode 100644 index 0000000..44cb04e --- /dev/null +++ b/src/dto/user.rs @@ -0,0 +1,13 @@ +pub struct User { + username: String, + email: String, +} + +impl User { + pub fn new(username: String, email: String) -> Self { + Self { + username, + email, + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 07682c6..9b43886 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use axum::middleware::Next; use axum::response::{Response, IntoResponse}; use axum::{Router, routing}; use axum::ServiceExt; +use bcrypt::Version; use tower_layer::Layer; use sqlx::sqlite::SqlitePoolOptions;