switch to private cookies + make them last 1 month
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
.history
|
||||
/frontend/node_modules/*
|
||||
db.sqlite
|
||||
config.toml
|
||||
|
166
Cargo.lock
generated
166
Cargo.lock
generated
@@ -17,6 +17,41 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@@ -263,6 +298,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[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 = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@@ -284,7 +329,11 @@ version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"subtle",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
@@ -360,9 +409,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
@@ -586,6 +645,16 @@ dependencies = [
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||
dependencies = [
|
||||
"opaque-debug",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
@@ -925,6 +994,15 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.9"
|
||||
@@ -1193,6 +1271,12 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
@@ -1282,6 +1366,18 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
@@ -1509,7 +1605,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"siphasher",
|
||||
"toml",
|
||||
"toml 0.8.23",
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
@@ -1631,6 +1727,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
@@ -2143,11 +2248,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"serde_spanned 0.6.9",
|
||||
"toml_datetime 0.6.11",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned 1.0.0",
|
||||
"toml_datetime 0.7.0",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
@@ -2157,6 +2277,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
@@ -2165,18 +2294,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"serde_spanned 0.6.9",
|
||||
"toml_datetime 0.6.11",
|
||||
"toml_write",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
@@ -2343,6 +2487,16 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
@@ -2520,7 +2674,9 @@ dependencies = [
|
||||
"rust-i18n",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"time",
|
||||
"tokio",
|
||||
"toml 0.9.5",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
@@ -5,13 +5,15 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8"
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie-private", "cookie"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
maud = { version = "0.27", features = ["axum"] }
|
||||
rust-i18n = "3.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "macros", "chrono"] }
|
||||
time = "0.3.41"
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
toml = "0.9.5"
|
||||
tower-http = { version = "0.6", features = ["fs"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
16
src/game.rs
16
src/game.rs
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
language::language,
|
||||
page::{MyMessage, Page},
|
||||
Backend, NameUpdateError,
|
||||
AppState, Backend, NameUpdateError,
|
||||
};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
@@ -10,7 +10,7 @@ use axum::{
|
||||
routing::{get, post},
|
||||
Form, Router,
|
||||
};
|
||||
use axum_extra::extract::CookieJar;
|
||||
use axum_extra::extract::PrivateCookieJar;
|
||||
use maud::{html, Markup, PreEscaped};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
@@ -18,7 +18,7 @@ use uuid::Uuid;
|
||||
|
||||
async fn index(
|
||||
State(backend): State<Arc<Backend>>,
|
||||
cookies: CookieJar,
|
||||
cookies: PrivateCookieJar,
|
||||
headers: HeaderMap,
|
||||
) -> Response {
|
||||
retu(backend, cookies, headers, None).await
|
||||
@@ -26,7 +26,7 @@ async fn index(
|
||||
|
||||
async fn retu(
|
||||
backend: Arc<Backend>,
|
||||
cookies: CookieJar,
|
||||
cookies: PrivateCookieJar,
|
||||
headers: HeaderMap,
|
||||
message: Option<MyMessage>,
|
||||
) -> Response {
|
||||
@@ -106,7 +106,7 @@ async fn retu(
|
||||
|
||||
async fn game(
|
||||
State(backend): State<Arc<Backend>>,
|
||||
cookies: CookieJar,
|
||||
cookies: PrivateCookieJar,
|
||||
headers: HeaderMap,
|
||||
Path(uuid): Path<String>,
|
||||
) -> Response {
|
||||
@@ -135,7 +135,7 @@ async fn game(
|
||||
retu(backend, cookies, headers, Some(message)).await
|
||||
}
|
||||
|
||||
async fn not_found(cookies: CookieJar, headers: HeaderMap) -> Markup {
|
||||
async fn not_found(cookies: PrivateCookieJar, headers: HeaderMap) -> Markup {
|
||||
let lang = language(&cookies, &headers);
|
||||
Page::new(lang).content(html! {
|
||||
h1 { (t!("not_found_title")) }
|
||||
@@ -149,7 +149,7 @@ struct NameForm {
|
||||
|
||||
async fn set_name(
|
||||
State(backend): State<Arc<Backend>>,
|
||||
cookies: CookieJar,
|
||||
cookies: PrivateCookieJar,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<NameForm>,
|
||||
) -> Response {
|
||||
@@ -179,7 +179,7 @@ async fn set_name(
|
||||
retu(backend, cookies, headers, Some(message)).await
|
||||
}
|
||||
|
||||
pub(super) fn routes() -> Router<Arc<Backend>> {
|
||||
pub(super) fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/game", get(index))
|
||||
.route("/game", post(set_name))
|
||||
|
@@ -1,9 +1,9 @@
|
||||
use crate::{language::language, page::Page};
|
||||
use axum::http::HeaderMap;
|
||||
use axum_extra::extract::CookieJar;
|
||||
use axum_extra::extract::PrivateCookieJar;
|
||||
use maud::{html, Markup, PreEscaped};
|
||||
|
||||
pub(super) async fn index(cookies: CookieJar, headers: HeaderMap) -> Markup {
|
||||
pub(super) async fn index(cookies: PrivateCookieJar, headers: HeaderMap) -> Markup {
|
||||
let lang = language(&cookies, &headers);
|
||||
rust_i18n::set_locale(lang.to_locale());
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use crate::Language;
|
||||
use axum::http::HeaderMap;
|
||||
use axum_extra::extract::CookieJar;
|
||||
use axum_extra::extract::PrivateCookieJar;
|
||||
|
||||
pub(crate) fn language(cookies: &CookieJar, headers: &HeaderMap) -> Language {
|
||||
pub(crate) fn language(cookies: &PrivateCookieJar, headers: &HeaderMap) -> Language {
|
||||
if let Some(lang_cookie) = cookies.clone().get("language") {
|
||||
// Return existing language cookie
|
||||
lang_cookie.value().to_string().into()
|
||||
|
82
src/main.rs
82
src/main.rs
@@ -1,13 +1,20 @@
|
||||
use crate::model::client::Client;
|
||||
use axum::{http::HeaderMap, routing::get, Router};
|
||||
use axum_extra::extract::{cookie::Cookie, CookieJar};
|
||||
use axum_extra::extract::{
|
||||
cookie::{Cookie, Expiration, Key},
|
||||
PrivateCookieJar,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, SqlitePool};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fmt::Display,
|
||||
fs,
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use tower_http::services::ServeDir;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
use uuid::Uuid;
|
||||
@@ -140,7 +147,7 @@ mod tests {
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
async fn client(&self, cookies: CookieJar) -> (CookieJar, Client) {
|
||||
async fn client(&self, cookies: PrivateCookieJar) -> (PrivateCookieJar, Client) {
|
||||
let existing_uuid = cookies
|
||||
.get("client_id")
|
||||
.and_then(|cookie| Uuid::parse_str(cookie.value()).ok());
|
||||
@@ -149,14 +156,24 @@ impl Backend {
|
||||
Some(uuid) => (cookies, self.get_client(&uuid).await),
|
||||
None => {
|
||||
let new_id = Uuid::new_v4();
|
||||
let updated_cookies = cookies.add(Cookie::new("client_id", new_id.to_string()));
|
||||
let expiration_date = OffsetDateTime::now_utc() + Duration::days(30);
|
||||
let mut cookie = Cookie::new("client_id", new_id.to_string());
|
||||
cookie.set_expires(Expiration::DateTime(expiration_date.into()));
|
||||
cookie.set_http_only(true);
|
||||
cookie.set_secure(true);
|
||||
|
||||
let updated_cookies = cookies.add(cookie);
|
||||
(updated_cookies, self.get_client(&new_id).await)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combined method for getting both client and language
|
||||
async fn client_full(&self, cookies: CookieJar, headers: &HeaderMap) -> (CookieJar, Req) {
|
||||
async fn client_full(
|
||||
&self,
|
||||
cookies: PrivateCookieJar,
|
||||
headers: &HeaderMap,
|
||||
) -> (PrivateCookieJar, Req) {
|
||||
let (cookies, client) = self.client(cookies).await;
|
||||
let lang = language::language(&cookies, headers);
|
||||
(cookies, Req { client, lang })
|
||||
@@ -190,6 +207,55 @@ impl Backend {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub(crate) backend: Arc<Backend>,
|
||||
pub key: Key,
|
||||
}
|
||||
|
||||
impl axum::extract::FromRef<AppState> for Key {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::extract::FromRef<AppState> for Arc<Backend> {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.backend.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Config {
|
||||
key: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn generate() -> Self {
|
||||
Self {
|
||||
key: Key::generate().master().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_or_create_key() -> Result<Key, Box<dyn std::error::Error>> {
|
||||
let config_path = "config.toml";
|
||||
|
||||
// Try to read existing config
|
||||
if Path::new(config_path).exists() {
|
||||
let content = fs::read_to_string(config_path)?;
|
||||
let config: Config = toml::from_str(&content)?;
|
||||
return Ok(Key::from(&config.key));
|
||||
}
|
||||
|
||||
// Create new config if file doesn't exist
|
||||
let config = Config::generate();
|
||||
let toml_string = toml::to_string(&config)?;
|
||||
fs::write(config_path, toml_string)?;
|
||||
|
||||
Ok(Key::from(&config.key))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::registry()
|
||||
@@ -203,11 +269,17 @@ async fn main() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let key = load_or_create_key().unwrap();
|
||||
let state = AppState {
|
||||
backend: Arc::new(Backend::Sqlite(db)),
|
||||
key,
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index::index))
|
||||
.nest_service("/static", ServeDir::new("./static/serve"))
|
||||
.merge(game::routes())
|
||||
.with_state(Arc::new(Backend::Sqlite(db)));
|
||||
.with_state(state);
|
||||
|
||||
// run our app with hyper, listening globally on port 3000
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
|
Reference in New Issue
Block a user