feat(api): implemented /account/register
This commit is contained in:
parent
0bb3e456be
commit
c13c70e6e4
|
@ -181,6 +181,21 @@ dependencies = [
|
|||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-httpauth"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d613edf08a42ccc6864c941d30fe14e1b676a77d16f1dbadc1174d065a0a775"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"base64",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
|
@ -239,6 +254,21 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.13"
|
||||
|
@ -293,6 +323,18 @@ version = "1.0.81"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "2.0.0"
|
||||
|
@ -350,6 +392,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"
|
||||
|
@ -380,6 +431,12 @@ dependencies = [
|
|||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
|
@ -417,6 +474,18 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
|
@ -927,17 +996,45 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icrc-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"actix-web-httpauth",
|
||||
"anyhow",
|
||||
"argon2",
|
||||
"compile-time-run",
|
||||
"dotenvy",
|
||||
"env_logger",
|
||||
"log",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -984,6 +1081,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
|
@ -1273,6 +1379,17 @@ dependencies = [
|
|||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
|
@ -1697,6 +1814,7 @@ dependencies = [
|
|||
"atoi",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
|
@ -1758,6 +1876,7 @@ dependencies = [
|
|||
"sha2",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
"syn 1.0.109",
|
||||
"tempfile",
|
||||
|
@ -1776,6 +1895,7 @@ dependencies = [
|
|||
"bitflags 2.4.2",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"dotenvy",
|
||||
|
@ -1817,6 +1937,7 @@ dependencies = [
|
|||
"base64",
|
||||
"bitflags 2.4.2",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
"dotenvy",
|
||||
"etcetera",
|
||||
|
@ -1852,6 +1973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"chrono",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -2121,6 +2243,15 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
@ -2145,6 +2276,60 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.5.1"
|
||||
|
@ -2155,6 +2340,15 @@ dependencies = [
|
|||
"wasite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
|
@ -13,6 +13,11 @@ log = "0.4.21"
|
|||
env_logger = "0.11.3"
|
||||
dotenvy = "0.15.7"
|
||||
compile-time-run = "0.2.12"
|
||||
uuid = { version = "1.7.0", features = ["v4"] }
|
||||
thiserror = "1.0.58"
|
||||
argon2 = "0.5.3"
|
||||
serde = { version = "1.0.197", features = ["default"] }
|
||||
|
||||
actix-web = "4.5.1"
|
||||
sqlx = { version = "0.7.4", features = ["runtime-tokio", "tls-native-tls", "mysql"] }
|
||||
actix-web-httpauth = "0.8.1"
|
||||
sqlx = { version = "0.7.4", features = ["runtime-tokio", "tls-native-tls", "mysql", "chrono"] }
|
||||
|
|
|
@ -1,6 +1,41 @@
|
|||
use actix_web::{post, HttpResponse, Responder};
|
||||
use crate::backend::{error::AccountRegisterError, Backend};
|
||||
use actix_web::{post, web, HttpResponse, Responder};
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AccountRegisterRequest {
|
||||
token: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AccountRegisterResponse {
|
||||
uuid: String,
|
||||
}
|
||||
|
||||
#[post("/account/register")]
|
||||
pub async fn account_register() -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
pub async fn account_register(
|
||||
backend: web::Data<Backend>,
|
||||
body: web::Json<AccountRegisterRequest>,
|
||||
) -> impl Responder {
|
||||
let body = body.into_inner();
|
||||
match backend.account_register(body.token, body.password).await {
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
Ok(res) => match res {
|
||||
Err(e) => match e {
|
||||
AccountRegisterError::InvalidToken => HttpResponse::Unauthorized().finish(),
|
||||
AccountRegisterError::SqlError(e) => {
|
||||
error!("{e}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
},
|
||||
Ok(uuid) => HttpResponse::Ok().json(AccountRegisterResponse {
|
||||
uuid: uuid.to_string(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ pub async fn start(config: &Config, backend: Backend) -> Result<()> {
|
|||
})
|
||||
.bind((config.addr.as_str(), config.port))?;
|
||||
|
||||
info!("API starting");
|
||||
if let Some(threads) = config.threads {
|
||||
server.workers(threads as usize).run().await?;
|
||||
} else {
|
||||
server.run().await?;
|
||||
}
|
||||
info!("API online");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
use sqlx::types::chrono::NaiveDateTime;
|
||||
|
||||
pub struct ActivationTokensRow {
|
||||
pub token: String,
|
||||
pub expire: NaiveDateTime,
|
||||
}
|
||||
|
||||
pub struct UsersRow {
|
||||
pub userid: String,
|
||||
pub password: String,
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AccountRegisterError {
|
||||
#[error("The given token is invalid")]
|
||||
InvalidToken,
|
||||
|
||||
#[error("SQL Error: {0}")]
|
||||
SqlError(sqlx::Error),
|
||||
}
|
|
@ -1,35 +1,138 @@
|
|||
mod db_structures;
|
||||
pub mod error;
|
||||
|
||||
use crate::backend::error::AccountRegisterError;
|
||||
use crate::config::Config;
|
||||
use anyhow::Result;
|
||||
use argon2::{
|
||||
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
||||
Argon2,
|
||||
};
|
||||
use db_structures::{ActivationTokensRow, UsersRow};
|
||||
use log::info;
|
||||
use sqlx::MySqlPool;
|
||||
use sqlx::{types::chrono::Utc, MySqlPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Backend {
|
||||
pool: MySqlPool,
|
||||
}
|
||||
|
||||
pub async fn start(config: &Config) -> Result<Backend> {
|
||||
let pool = MySqlPool::connect(config.database.as_str()).await?.into();
|
||||
impl Backend {
|
||||
pub async fn start(config: &Config) -> Result<Self> {
|
||||
let pool = MySqlPool::connect(config.database.as_str()).await?.into();
|
||||
|
||||
sqlx::query!(
|
||||
r#"CREATE TABLE IF NOT EXISTS Users (
|
||||
username VARCHAR(32) NOT NULL PRIMARY KEY,
|
||||
password VARCHAR(98) NOT NULL
|
||||
sqlx::query!(
|
||||
r#"CREATE TABLE IF NOT EXISTS Users (
|
||||
userid UUID NOT NULL PRIMARY KEY,
|
||||
password VARCHAR(128) NOT NULL
|
||||
);"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"CREATE TABLE IF NOT EXISTS Relays (
|
||||
id BINARY(16) NOT NULL PRIMARY KEY,
|
||||
secret VARCHAR(98) NOT NULL
|
||||
sqlx::query!(
|
||||
r#"CREATE TABLE IF NOT EXISTS Relays (
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
secret VARCHAR(128) NOT NULL
|
||||
);"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
info!("Backend initialized");
|
||||
sqlx::query!(
|
||||
r#"CREATE TABLE IF NOT EXISTS ActivationTokens (
|
||||
token CHAR(48) NOT NULL PRIMARY KEY,
|
||||
expire DATETIME NOT NULL
|
||||
);"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(Backend { pool })
|
||||
sqlx::query!(
|
||||
r#"CREATE TABLE IF NOT EXISTS AuthTokens (
|
||||
token CHAR(48) NOT NULL PRIMARY KEY,
|
||||
userid UUID NOT NULL,
|
||||
exipre DATETIME NOT NULL
|
||||
);"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
info!("Backend initialized");
|
||||
|
||||
Ok(Self { pool })
|
||||
}
|
||||
|
||||
async fn check_activation_token(&self, token: &str) -> Result<bool, sqlx::Error> {
|
||||
match sqlx::query_as!(
|
||||
ActivationTokensRow,
|
||||
r#"SELECT * FROM ActivationTokens WHERE token = ?;"#,
|
||||
token
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
{
|
||||
Err(e) => match e {
|
||||
sqlx::Error::RowNotFound => Ok(false),
|
||||
_ => Err(e),
|
||||
},
|
||||
Ok(row) => {
|
||||
sqlx::query!(r#"DELETE FROM ActivationTokens WHERE token = ?;"#, token)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
if row.expire > Utc::now().naive_utc() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_user(&self, userid: Uuid) -> Result<Option<()>, sqlx::Error> {
|
||||
match sqlx::query_as!(
|
||||
UsersRow,
|
||||
r#"SELECT * FROM Users WHERE userid = ?;"#,
|
||||
userid.as_bytes().as_slice()
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
{
|
||||
Err(e) => match e {
|
||||
sqlx::Error::RowNotFound => Ok(None),
|
||||
_ => Err(e),
|
||||
},
|
||||
Ok(_row) => Ok(Some(())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn account_register(
|
||||
&self,
|
||||
token: String,
|
||||
password: String,
|
||||
) -> Result<Result<Uuid, error::AccountRegisterError>> {
|
||||
if !self.check_activation_token(&token).await? {
|
||||
return Ok(Err(AccountRegisterError::InvalidToken));
|
||||
}
|
||||
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
||||
let hash = Argon2::default()
|
||||
.hash_password(password.as_bytes(), &salt)
|
||||
.map_err(|_| anyhow::Error::msg("Failed to hash the password"))?
|
||||
.to_string();
|
||||
|
||||
let userid = Uuid::new_v4();
|
||||
|
||||
sqlx::query!(
|
||||
r#"INSERT INTO Users VALUES (?, ?);"#,
|
||||
userid.as_bytes().as_slice(),
|
||||
hash
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(Ok(userid))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ mod api;
|
|||
mod backend;
|
||||
mod config;
|
||||
|
||||
use crate::backend::Backend;
|
||||
use anyhow::Result;
|
||||
use compile_time_run::run_command_str;
|
||||
use config::Config;
|
||||
|
@ -23,7 +24,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
println!("{config:#?}");
|
||||
|
||||
let backend = backend::start(&config).await?;
|
||||
let backend = Backend::start(&config).await?;
|
||||
api::start(&config, backend).await?;
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue