diff --git a/.gitignore b/.gitignore index 4a39e32..53c8640 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .fleet # Rust stuff -target \ No newline at end of file +target + +start.sh diff --git a/.sqlx/query-7009cdfba0b6c8d7c8ed9d49bf91905d159c3bbfe0c8e78876a8cd2b952e28b5.json b/.sqlx/query-0d30b5067f0a9c1c9929882a7c8b7cb5511d4808b6ee0ffd3201ed3772f0b191.json similarity index 85% rename from .sqlx/query-7009cdfba0b6c8d7c8ed9d49bf91905d159c3bbfe0c8e78876a8cd2b952e28b5.json rename to .sqlx/query-0d30b5067f0a9c1c9929882a7c8b7cb5511d4808b6ee0ffd3201ed3772f0b191.json index c08a556..2255999 100644 --- a/.sqlx/query-7009cdfba0b6c8d7c8ed9d49bf91905d159c3bbfe0c8e78876a8cd2b952e28b5.json +++ b/.sqlx/query-0d30b5067f0a9c1c9929882a7c8b7cb5511d4808b6ee0ffd3201ed3772f0b191.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n id,\n username,\n email,\n salt,\n password,\n joined,\n verified as \"verified!: bool\",\n follows,\n followers,\n permissions\n FROM Accounts WHERE id = $1;", + "query": "SELECT\n id,\n username,\n email,\n salt,\n password,\n joined,\n verified as \"verified!: bool\",\n follows,\n followers,\n flags\n FROM Accounts WHERE id = $1;", "describe": { "columns": [ { @@ -16,7 +16,7 @@ { "ordinal": 2, "name": "email", - "type_info": "Text" + "type_info": "Varchar" }, { "ordinal": 3, @@ -50,8 +50,8 @@ }, { "ordinal": 9, - "name": "permissions", - "type_info": "Int8" + "name": "flags", + "type_info": "Int2" } ], "parameters": { @@ -72,5 +72,5 @@ false ] }, - "hash": "7009cdfba0b6c8d7c8ed9d49bf91905d159c3bbfe0c8e78876a8cd2b952e28b5" + "hash": "0d30b5067f0a9c1c9929882a7c8b7cb5511d4808b6ee0ffd3201ed3772f0b191" } diff --git a/.sqlx/query-71b834dfe4384a536257b888e293b046a70aefd4ac2dcbf3268e34c9576e903e.json b/.sqlx/query-128da462eb065680f50c9c3fef93d49974ba1e0697d5a996736e3715e335e37d.json similarity index 84% rename from .sqlx/query-71b834dfe4384a536257b888e293b046a70aefd4ac2dcbf3268e34c9576e903e.json rename to .sqlx/query-128da462eb065680f50c9c3fef93d49974ba1e0697d5a996736e3715e335e37d.json index fe096c9..b6f51ad 100644 --- a/.sqlx/query-71b834dfe4384a536257b888e293b046a70aefd4ac2dcbf3268e34c9576e903e.json +++ b/.sqlx/query-128da462eb065680f50c9c3fef93d49974ba1e0697d5a996736e3715e335e37d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n id,\n username,\n email,\n salt,\n password,\n joined,\n verified as \"verified!: bool\",\n follows,\n followers,\n permissions\n FROM Accounts WHERE email = $1;", + "query": "SELECT\n id,\n username,\n email,\n salt,\n password,\n joined,\n verified as \"verified!: bool\",\n follows,\n followers,\n flags\n FROM Accounts WHERE email = $1;", "describe": { "columns": [ { @@ -16,7 +16,7 @@ { "ordinal": 2, "name": "email", - "type_info": "Text" + "type_info": "Varchar" }, { "ordinal": 3, @@ -50,8 +50,8 @@ }, { "ordinal": 9, - "name": "permissions", - "type_info": "Int8" + "name": "flags", + "type_info": "Int2" } ], "parameters": { @@ -72,5 +72,5 @@ false ] }, - "hash": "71b834dfe4384a536257b888e293b046a70aefd4ac2dcbf3268e34c9576e903e" + "hash": "128da462eb065680f50c9c3fef93d49974ba1e0697d5a996736e3715e335e37d" } diff --git a/.sqlx/query-0e894c84befe397687d4820ea313aa937cf76286e98f7e66c342fb87a3896265.json b/.sqlx/query-294103135f5b8550f1de72a04ee9ff7569e89b2e72c2e9628c4b8a493468f99d.json similarity index 84% rename from .sqlx/query-0e894c84befe397687d4820ea313aa937cf76286e98f7e66c342fb87a3896265.json rename to .sqlx/query-294103135f5b8550f1de72a04ee9ff7569e89b2e72c2e9628c4b8a493468f99d.json index 0e9e556..7944d2f 100644 --- a/.sqlx/query-0e894c84befe397687d4820ea313aa937cf76286e98f7e66c342fb87a3896265.json +++ b/.sqlx/query-294103135f5b8550f1de72a04ee9ff7569e89b2e72c2e9628c4b8a493468f99d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n id,\n username,\n email,\n salt,\n password,\n joined,\n verified as \"verified!: bool\",\n follows,\n followers,\n permissions\n FROM Accounts WHERE username = $1;", + "query": "SELECT\n id,\n username,\n email,\n salt,\n password,\n joined,\n verified as \"verified!: bool\",\n follows,\n followers,\n flags\n FROM Accounts WHERE username = $1;", "describe": { "columns": [ { @@ -16,7 +16,7 @@ { "ordinal": 2, "name": "email", - "type_info": "Text" + "type_info": "Varchar" }, { "ordinal": 3, @@ -50,8 +50,8 @@ }, { "ordinal": 9, - "name": "permissions", - "type_info": "Int8" + "name": "flags", + "type_info": "Int2" } ], "parameters": { @@ -72,5 +72,5 @@ false ] }, - "hash": "0e894c84befe397687d4820ea313aa937cf76286e98f7e66c342fb87a3896265" + "hash": "294103135f5b8550f1de72a04ee9ff7569e89b2e72c2e9628c4b8a493468f99d" } diff --git a/.sqlx/query-8b37c356c1df9961b47359a56f69b658adafa9c58b79e6dd867f6e4f62fbd144.json b/.sqlx/query-4919f945175894aac7c9a49c948876996b35dc3ffe772856de59443c2a1b9b64.json similarity index 62% rename from .sqlx/query-8b37c356c1df9961b47359a56f69b658adafa9c58b79e6dd867f6e4f62fbd144.json rename to .sqlx/query-4919f945175894aac7c9a49c948876996b35dc3ffe772856de59443c2a1b9b64.json index c55d4bf..f76f236 100644 --- a/.sqlx/query-8b37c356c1df9961b47359a56f69b658adafa9c58b79e6dd867f6e4f62fbd144.json +++ b/.sqlx/query-4919f945175894aac7c9a49c948876996b35dc3ffe772856de59443c2a1b9b64.json @@ -1,12 +1,12 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO Accounts (username, email, salt, password, joined, verified, permissions) VALUES ($1, $2, $3, $4, $5, false, 0);", + "query": "INSERT INTO Accounts (username, email, salt, password, joined, verified, flags) VALUES ($1, $2, $3, $4, $5, false, 0);", "describe": { "columns": [], "parameters": { "Left": [ "Varchar", - "Text", + "Varchar", "Varchar", "Varchar", "Timestamp" @@ -14,5 +14,5 @@ }, "nullable": [] }, - "hash": "8b37c356c1df9961b47359a56f69b658adafa9c58b79e6dd867f6e4f62fbd144" + "hash": "4919f945175894aac7c9a49c948876996b35dc3ffe772856de59443c2a1b9b64" } diff --git a/.sqlx/query-50c529b57bcd8d46a5160f07fe11ce40e1db91fab254f3604e5b08587fe7caf2.json b/.sqlx/query-50c529b57bcd8d46a5160f07fe11ce40e1db91fab254f3604e5b08587fe7caf2.json deleted file mode 100644 index 7d0e292..0000000 --- a/.sqlx/query-50c529b57bcd8d46a5160f07fe11ce40e1db91fab254f3604e5b08587fe7caf2.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n CREATE TABLE IF NOT EXISTS Accounts (\n id SERIAL8 NOT NULL,\n\t username VARCHAR(32) NOT NULL,\n email TEXT NOT NULL,\n\t salt VARCHAR(22) NOT NULL,\n\t password VARCHAR(96) NOT NULL,\n joined TIMESTAMP NOT NULL,\n verified BOOLEAN NOT NULL,\n follows BIGINT[],\n followers BIGINT[],\n flags INT2 NOT NULL,\n\t PRIMARY KEY(id)\n );\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "50c529b57bcd8d46a5160f07fe11ce40e1db91fab254f3604e5b08587fe7caf2" -} diff --git a/.sqlx/query-56757ca46f6c90c12658dcaf2a64aca3d15ae6540bfafac0db23848c1aa32ba4.json b/.sqlx/query-56757ca46f6c90c12658dcaf2a64aca3d15ae6540bfafac0db23848c1aa32ba4.json new file mode 100644 index 0000000..f9baf8d --- /dev/null +++ b/.sqlx/query-56757ca46f6c90c12658dcaf2a64aca3d15ae6540bfafac0db23848c1aa32ba4.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "\n CREATE TABLE IF NOT EXISTS Accounts (\n id SERIAL8 NOT NULL,\n\t username VARCHAR(32) NOT NULL,\n email VARCHAR(44) NOT NULL,\n\t salt VARCHAR(22) NOT NULL,\n\t password VARCHAR(128) NOT NULL,\n joined TIMESTAMP NOT NULL,\n verified BOOLEAN NOT NULL,\n follows BIGINT[],\n followers BIGINT[],\n flags INT2 NOT NULL,\n\t PRIMARY KEY(id)\n );\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "56757ca46f6c90c12658dcaf2a64aca3d15ae6540bfafac0db23848c1aa32ba4" +} diff --git a/Cargo.lock b/Cargo.lock index 04579b1..8a28bff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash 0.8.3", - "base64 0.21.2", + "base64 0.21.3", "bitflags 1.3.2", "brotli", "bytes", @@ -347,6 +347,18 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +[[package]] +name = "argon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash 0.5.0", +] + [[package]] name = "async-trait" version = "0.1.73" @@ -402,9 +414,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" [[package]] name = "base64ct" @@ -449,6 +461,15 @@ dependencies = [ "serde", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -1603,13 +1624,14 @@ dependencies = [ "actix-web", "actix-web-httpauth", "anyhow", + "argon2", + "base64 0.21.3", "chrono", "clap", "env_logger", "libinjection", "log", "mail-send", - "pbkdf2 0.12.2", "regex", "serde", "sha2", @@ -1782,18 +1804,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", - "password-hash 0.5.0", - "sha2", -] - [[package]] name = "peeking_take_while" version = "0.1.2" @@ -2092,7 +2102,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", ] [[package]] @@ -2410,7 +2420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482" dependencies = [ "atoi", - "base64 0.21.2", + "base64 0.21.3", "bitflags 2.4.0", "byteorder", "bytes", @@ -2453,7 +2463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e" dependencies = [ "atoi", - "base64 0.21.2", + "base64 0.21.3", "bitflags 2.4.0", "byteorder", "chrono", @@ -3157,7 +3167,7 @@ dependencies = [ "crossbeam-utils", "flate2", "hmac", - "pbkdf2 0.11.0", + "pbkdf2", "sha1", "time 0.3.25", "zstd 0.11.2+zstd.1.5.2", diff --git a/Cargo.toml b/Cargo.toml index 38d5999..df4e056 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ actix-web = "4" serde = { version = "1.0.183", features = ["derive"] } libinjection = "0.3.2" sha2 = "0.10.2" -pbkdf2 = { version = "0.12", features = ["simple"] } env_logger = "0.10" log = "0.4" clap = { version = "4.3.21", features = ["derive"] } @@ -22,3 +21,5 @@ uuid = { version = "1.4.1", features = ["v4"] } chrono = "0.4" mail-send = "0.4.0" regex = "1.9.3" +argon2 = "0.5.2" +base64 = "0.21.3" diff --git a/src/accounts.rs b/src/accounts.rs index bf7a352..d4d7996 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -1,8 +1,10 @@ use anyhow::{Error, Result}; -use pbkdf2::{ +use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, - Pbkdf2, + Argon2, }; +use base64; +use sha2::{Digest, Sha256}; use sqlx::{postgres::PgPool, types::chrono as sqlx_chrono}; #[derive(Debug, Clone, Copy)] @@ -22,7 +24,7 @@ pub struct Account { pub verified: bool, pub follows: Option>, pub followers: Option>, - pub permissions: i64, + pub flags: i16, } impl From for ID { @@ -64,19 +66,25 @@ impl Account { password: &String, ) -> Result { let salt = SaltString::generate(&mut OsRng); - let hash = Pbkdf2 + let password_hash = Argon2::default() .hash_password(password.as_bytes(), &salt) .map_err(|_| anyhow::Error::msg("Failed to hash the password"))? .to_string(); + let email_hash = { + let mut hasher = Sha256::new(); + hasher.update(email.as_bytes()); + base64::encode(hasher.finalize()) + }; + let joined = sqlx_chrono::Utc::now().naive_utc(); sqlx::query!( - r#"INSERT INTO Accounts (username, email, salt, password, joined, verified, permissions) VALUES ($1, $2, $3, $4, $5, false, 0);"#, + r#"INSERT INTO Accounts (username, email, salt, password, joined, verified, flags) VALUES ($1, $2, $3, $4, $5, false, 0);"#, username, - email, + email_hash, salt.to_string(), - hash, + password_hash, joined, ) .execute(pool) @@ -103,7 +111,7 @@ impl Account { verified as "verified!: bool", follows, followers, - permissions + flags FROM Accounts WHERE username = $1;"#, username ) @@ -129,7 +137,7 @@ impl Account { verified as "verified!: bool", follows, followers, - permissions + flags FROM Accounts WHERE id = $1;"#, id ) @@ -143,6 +151,12 @@ impl Account { } pub async fn from_email(pool: &PgPool, email: &String) -> Result> { + let email_hash = { + let mut hasher = Sha256::new(); + hasher.update(email.as_bytes()); + base64::encode(hasher.finalize()) + }; + match sqlx::query_as!( Self, r#"SELECT @@ -155,9 +169,9 @@ impl Account { verified as "verified!: bool", follows, followers, - permissions + flags FROM Accounts WHERE email = $1;"#, - email + email_hash ) .fetch_one(pool) .await @@ -172,16 +186,19 @@ impl Account { let hash = PasswordHash::new(self.password.as_str()) .map_err(|_| anyhow::Error::msg("Failed to parse the password hash"))?; - match Pbkdf2.verify_password(password.as_bytes(), &hash) { + match Argon2::default().verify_password(password.as_bytes(), &hash) { Ok(_) => Ok(true), Err(_) => Ok(false), } } pub async fn delete(&self, pool: &PgPool) -> Result<()> { - sqlx::query!(r#"DELETE FROM AuthTokens WHERE account = $1;"#, self.id.id()) - .execute(pool) - .await?; + sqlx::query!( + r#"DELETE FROM AuthTokens WHERE account = $1;"#, + self.id.id() + ) + .execute(pool) + .await?; sqlx::query!(r#"DELETE FROM Accounts WHERE id = $1;"#, self.id.id()) .execute(pool) diff --git a/src/api/account/handlers.rs b/src/api/account/handlers.rs index 446c36d..216843c 100644 --- a/src/api/account/handlers.rs +++ b/src/api/account/handlers.rs @@ -25,7 +25,7 @@ pub async fn register( return Ok(data::RegisterResponse::MalformedEmail); } - // Check if the usernam is already taken + // Check if the username is already taken if let Some(account) = Account::from_username(pool, &request.username).await? { // Check if the account that has taken the username is verified or has an open verification request. if account.verified diff --git a/src/api/mod.rs b/src/api/mod.rs index 975ba1a..7cf8396 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -16,16 +16,16 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> { sqlx::query!( r#" CREATE TABLE IF NOT EXISTS Accounts ( - id SERIAL8 NOT NULL, - username VARCHAR(32) NOT NULL, - email TEXT NOT NULL, - salt VARCHAR(22) NOT NULL, - password VARCHAR(96) NOT NULL, - joined TIMESTAMP NOT NULL, - verified BOOLEAN NOT NULL, + id SERIAL8 NOT NULL, + username VARCHAR(32) NOT NULL, + email VARCHAR(44) NOT NULL, + salt VARCHAR(22) NOT NULL, + password VARCHAR(128) NOT NULL, + joined TIMESTAMP NOT NULL, + verified BOOLEAN NOT NULL, follows BIGINT[], followers BIGINT[], - flags INT2 NOT NULL, + flags INT2 NOT NULL, PRIMARY KEY(id) ); "#