:3
This commit is contained in:
parent
81e68770c6
commit
111bcedf2c
118
Cargo.lock
generated
118
Cargo.lock
generated
|
@ -44,6 +44,18 @@ version = "1.0.99"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[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 = "askama"
|
||||
version = "0.14.0"
|
||||
|
@ -233,6 +245,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"
|
||||
|
@ -626,6 +647,15 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
|
@ -1121,7 +1151,7 @@ dependencies = [
|
|||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
@ -1206,6 +1236,17 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
|
@ -1226,11 +1267,16 @@ name = "persmgr-gui"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
"askama",
|
||||
"axum",
|
||||
"base64",
|
||||
"pulldown-cmark",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"time",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower",
|
||||
|
@ -1314,6 +1360,25 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getopts",
|
||||
"memchr",
|
||||
"pulldown-cmark-escape",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark-escape"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
|
@ -1336,8 +1401,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1347,7 +1422,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1359,6 +1444,15 @@ dependencies = [
|
|||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
|
@ -1381,7 +1475,7 @@ dependencies = [
|
|||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
|
@ -1534,7 +1628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1695,7 +1789,7 @@ dependencies = [
|
|||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha1",
|
||||
|
@ -1733,7 +1827,7 @@ dependencies = [
|
|||
"md-5",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
|
@ -2121,7 +2215,7 @@ dependencies = [
|
|||
"futures",
|
||||
"http",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
|
@ -2239,6 +2333,12 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
|
|
|
@ -5,11 +5,16 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.99"
|
||||
argon2 = { version = "0.5.3", features = ["simple", "std"] }
|
||||
askama = "0.14.0"
|
||||
axum = "0.8.4"
|
||||
base64 = "0.22.1"
|
||||
pulldown-cmark = "0.13.0"
|
||||
rand = "0.9.2"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.143"
|
||||
sqlx = { version = "0.8.6", features = ["macros", "postgres", "runtime-tokio"] }
|
||||
time = "0.3.43"
|
||||
tokio = { version = "1.47.1", features = ["full"] }
|
||||
toml = "0.9.5"
|
||||
tower = { version = "0.5.2", features = ["full"] }
|
||||
|
|
3
migrations/20250906202709_sessions.down.sql
Normal file
3
migrations/20250906202709_sessions.down.sql
Normal file
|
@ -0,0 +1,3 @@
|
|||
-- Add down migration script here
|
||||
|
||||
DROP TABLE IF EXISTS sessions;
|
7
migrations/20250906202709_sessions.up.sql
Normal file
7
migrations/20250906202709_sessions.up.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
-- Add up migration script here
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
user_id BIGINT NOT NULL,
|
||||
session_key TEXT NOT NULL UNIQUE,
|
||||
expires BIGINT NOT NULL
|
||||
)
|
|
@ -1,17 +1,82 @@
|
|||
use axum::{
|
||||
body::Body,
|
||||
extract::State,
|
||||
http::{HeaderMap, HeaderValue, StatusCode},
|
||||
response::{IntoResponse, Response},
|
||||
use argon2::{
|
||||
Argon2, Params,
|
||||
password_hash::{PasswordHasher, SaltString, rand_core::OsRng},
|
||||
};
|
||||
use axum::{
|
||||
extract::{Json, State},
|
||||
http::{Response, StatusCode},
|
||||
};
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use serde::Deserialize;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
use crate::db::Database;
|
||||
|
||||
pub async fn route(State(db): State<Database>) -> Response {
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ReqBody {
|
||||
email: String,
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
pub async fn route(State(db): State<Database>, Json(body): Json<ReqBody>) -> Response<String> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
||||
let argon2 = Argon2::new(
|
||||
argon2::Algorithm::Argon2id,
|
||||
argon2::Version::V0x13,
|
||||
Params::DEFAULT,
|
||||
);
|
||||
let hash = argon2
|
||||
.hash_password(body.password.as_bytes(), salt.as_salt())
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let mut user = crate::db::tables::user::User::default();
|
||||
user.username = body.username;
|
||||
user.email = body.email;
|
||||
user.pw_salt = salt.to_string();
|
||||
user.pw_hash = hash;
|
||||
|
||||
if let Err(e) = user.insert_new(&db.pool()).await {
|
||||
return Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(format!("ERROR: Failed to create user: {e}"))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let Ok(user) = crate::db::tables::user::User::get_by_username(&db.pool(), user.username).await
|
||||
else {
|
||||
return Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(String::from("ERROR: Failed to get created user"))
|
||||
.unwrap();
|
||||
};
|
||||
|
||||
let session_key = {
|
||||
let mut buf = [0u8; 32];
|
||||
rand::fill(&mut buf);
|
||||
general_purpose::STANDARD.encode(&buf)
|
||||
};
|
||||
|
||||
let mut session = crate::db::tables::sessions::Session::default();
|
||||
session.user_id = user.id;
|
||||
session.session_key = session_key;
|
||||
session.expires = OffsetDateTime::now_utc()
|
||||
.saturating_add(Duration::days(30))
|
||||
.unix_timestamp();
|
||||
|
||||
if let Err(e) = session.insert_new(db.pool()).await {
|
||||
return Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(format!("ERROR: Failed to create session for user: {e}",))
|
||||
.unwrap();
|
||||
};
|
||||
|
||||
Response::builder()
|
||||
.header("Location", "/")
|
||||
.header("Set-Cookie", &format!("session=meowmeowmeow"))
|
||||
.header("Set-Cookie", &format!("session={}", session.session_key))
|
||||
.status(StatusCode::SEE_OTHER)
|
||||
.body(Body::empty())
|
||||
.body(String::new())
|
||||
.unwrap()
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod sessions;
|
||||
pub mod user;
|
||||
|
|
58
src/db/tables/sessions.rs
Normal file
58
src/db/tables/sessions.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use crate::db::CurrPool;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Session {
|
||||
pub user_id: i64,
|
||||
pub session_key: String,
|
||||
pub expires: i64,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub async fn insert_new(&self, pool: &CurrPool) -> Result<Self> {
|
||||
let session = sqlx::query_as!(
|
||||
Session,
|
||||
r#"
|
||||
INSERT INTO sessions (user_id, session_key, expires)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *
|
||||
"#,
|
||||
self.user_id,
|
||||
self.session_key,
|
||||
self.expires
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(session)
|
||||
}
|
||||
pub async fn get_by_username(pool: &CurrPool, user_id: i64) -> anyhow::Result<Self> {
|
||||
let session = sqlx::query_as!(
|
||||
Session,
|
||||
"SELECT * FROM sessions WHERE user_id = $1",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
Ok(session)
|
||||
}
|
||||
pub async fn get_by_session_key(pool: &CurrPool, session_key: String) -> anyhow::Result<Self> {
|
||||
let session = sqlx::query_as!(
|
||||
Session,
|
||||
"SELECT * FROM sessions WHERE session_key = $1",
|
||||
session_key
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
pub async fn remove_old_sessions(pool: &CurrPool) -> anyhow::Result<()> {
|
||||
let curr_time = time::OffsetDateTime::now_utc().unix_timestamp();
|
||||
sqlx::query!("DELETE FROM sessions WHERE expires < $1", curr_time)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user