From 1576062160d1c05fb6379644e830507124b25412 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Sun, 5 Jul 2026 20:30:56 +0000 Subject: [PATCH 01/32] Update Rust crate ecdsa to 0.17.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a894704..832368b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ lettre = "0.11.15" rand = { version = "0.10.0", features = ["std"] } reqwest = "0.13.0" binrw = "0.15.1" -ecdsa = { version = "0.16.9", features = ["pem", "std", "verifying"] } +ecdsa = { version = "0.17.0", features = ["pem", "std", "verifying"] } sha256 = "1.6.0" p256 = "0.13.2" k256 = "0.13.4" From f32e69be99fa47b9c6be81761b15f93e6f3f3fb8 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Wed, 15 Jul 2026 17:37:53 +0200 Subject: [PATCH 02/32] serial --- src/account/account.rs | 24 +++-- src/json_api/oauth/generate_token.rs | 106 ++++++++++++-------- src/nnid/oauth/generate_token.rs | 139 +++++++++++++++------------ src/nnid/provider.rs | 60 +++++++++++- 4 files changed, 219 insertions(+), 110 deletions(-) diff --git a/src/account/account.rs b/src/account/account.rs index fd58a1b..41c285a 100644 --- a/src/account/account.rs +++ b/src/account/account.rs @@ -117,7 +117,10 @@ pub fn generate_password(pid: i32, cleartext_password: &str) -> Option { bcrypt::hash(password, 10).ok() } -pub async fn read_basic_auth_token(connection: &Pool, token: &str) -> Option { +pub async fn read_basic_auth_token( + connection: &Pool, + token: &str, +) -> Option<(User, Option<[u8; 32]>)> { let data = match BASE64_STANDARD.decode(&token) { Ok(d) => d, Err(e) => { @@ -161,13 +164,16 @@ pub async fn read_basic_auth_token(connection: &Pool, token: &str) -> Option Option { +pub async fn read_bearer_auth_token( + connection: &Pool, + token: &str, +) -> Option<(User, Option<[u8; 32]>)> { let data = TokenData::decode(token)?; let token_info = sqlx::query!( @@ -189,11 +195,11 @@ pub async fn read_bearer_auth_token(connection: &Pool, token: &str) -> Option String { - let mut rng = rand::rng(); + let mut rng = rand::rng(); let mut output = String::with_capacity(16); while output.len() < 16 { @@ -214,6 +220,7 @@ pub fn generate_nex_password() -> String { pub struct Auth( pub User, + pub Option<[u8; 32]>, ); impl AsRef @@ -278,6 +285,7 @@ pub async fn link_certificate_to_pid( Ok(()) } +// todo: make this more consistent by requiring a cert to be asociated to the token if USE_CERT is set #[async_trait] impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> for Auth @@ -297,13 +305,13 @@ impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> let (auth_type, token) = request_try!(auth.split_once(' ').ok_or(INVALID_TOKEN_ERRORS)); - let user = match auth_type { + let data = match auth_type { "Basic" if !FORCE_BEARER_AUTH => read_basic_auth_token(pool, token).await, "Bearer" => read_bearer_auth_token(pool, token).await, _ => return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)), }; - let Some(user) = user else { + let Some((user, cert)) = data else { return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)); }; @@ -316,7 +324,7 @@ impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> // ..user // }; - Outcome::Success(Self(user)) + Outcome::Success(Self(user, cert)) } } diff --git a/src/json_api/oauth/generate_token.rs b/src/json_api/oauth/generate_token.rs index 72843d7..767e374 100644 --- a/src/json_api/oauth/generate_token.rs +++ b/src/json_api/oauth/generate_token.rs @@ -1,19 +1,18 @@ use crate::Pool; -use serde::Serialize; -use rocket::FromForm; -use sha2::{Sha256, Digest}; -use bytemuck::bytes_of; -use rocket::{post, State, form::Form, http::Status, serde::json::Json}; -use sqlx::Row; -use std::env; -use once_cell::sync::Lazy; use crate::nnid::oauth::generate_token::create_token; use crate::nnid::oauth::generate_token::token_type::AUTH_TOKEN; +use bytemuck::bytes_of; use chrono::Utc; +use once_cell::sync::Lazy; +use rocket::FromForm; +use rocket::{State, form::Form, http::Status, post, serde::json::Json}; +use serde::Serialize; +use sha2::{Digest, Sha256}; +use sqlx::Row; +use std::env; -pub static CLIENT_SECRET: Lazy = Lazy::new(|| { - env::var("OAUTH_CLIENT_SECRET").expect("OAUTH_CLIENT_SECRET not set") -}); +pub static CLIENT_SECRET: Lazy = + Lazy::new(|| env::var("OAUTH_CLIENT_SECRET").expect("OAUTH_CLIENT_SECRET not set")); #[derive(Serialize)] pub struct OAuthTokenResponse { @@ -52,17 +51,18 @@ pub struct OAuthErrorResponse { #[post("/api/v2/oauth2/generate_token", data = "")] pub async fn generate_token( - pool: &State, - form_data: Form> + pool: &State, + form_data: Form>, ) -> Result, (Status, Json)> { - match form_data.client_id { Some("account") | Some("splatnet") => { let cl_secret: String = CLIENT_SECRET.clone(); if form_data.client_secret != Some(&cl_secret) { return Err(( Status::Unauthorized, - Json(OAuthErrorResponse { error: "invalid_client".to_string() }) + Json(OAuthErrorResponse { + error: "invalid_client".to_string(), + }), )); } } @@ -72,7 +72,9 @@ pub async fn generate_token( _ => { return Err(( Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_client".to_string() }) + Json(OAuthErrorResponse { + error: "invalid_client".to_string(), + }), )); } } @@ -82,23 +84,31 @@ pub async fn generate_token( "password" => { let username = form_data.username.ok_or(( Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_request".to_string() }) + Json(OAuthErrorResponse { + error: "invalid_request".to_string(), + }), ))?; let password = form_data.password.ok_or(( Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_request".to_string() }) + Json(OAuthErrorResponse { + error: "invalid_request".to_string(), + }), ))?; let user_row = match sqlx::query("SELECT pid, password FROM users WHERE username = $1") .bind(username) .fetch_optional(pool.inner()) - .await + .await { Ok(Some(row)) => row, - _ => return Err(( - Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_grant".to_string() }) - )), + _ => { + return Err(( + Status::BadRequest, + Json(OAuthErrorResponse { + error: "invalid_grant".to_string(), + }), + )); + } }; let db_pid: i32 = user_row.get("pid"); @@ -107,31 +117,39 @@ pub async fn generate_token( if !verify_nintendo_password(db_pid, password, &db_bcrypt_hash) { return Err(( Status::Unauthorized, - Json(OAuthErrorResponse { error: "invalid_grant".to_string() }) + Json(OAuthErrorResponse { + error: "invalid_grant".to_string(), + }), )); } db_pid - }, + } "authorization_code" => { let incoming_code = form_data.code.ok_or(( - Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_request".to_string() }) + Status::BadRequest, + Json(OAuthErrorResponse { + error: "invalid_request".to_string(), + }), ))?; - + let code_row = match sqlx::query( - "SELECT pid, expires_at, used FROM oauth_auth_codes WHERE code = $1" + "SELECT pid, expires_at, used FROM oauth_auth_codes WHERE code = $1", ) .bind(incoming_code) .fetch_optional(pool.inner()) .await { Ok(Some(row)) => row, - _ => return Err(( - Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_grant".to_string() }) - )), + _ => { + return Err(( + Status::BadRequest, + Json(OAuthErrorResponse { + error: "invalid_grant".to_string(), + }), + )); + } }; let is_used: bool = code_row.get("used"); @@ -140,8 +158,10 @@ pub async fn generate_token( if is_used || expires_at < Utc::now().naive_utc() { return Err(( - Status::BadRequest, - Json(OAuthErrorResponse { error: "invalid_grant".to_string() }) + Status::BadRequest, + Json(OAuthErrorResponse { + error: "invalid_grant".to_string(), + }), )); } @@ -151,15 +171,19 @@ pub async fn generate_token( .await; target_pid - }, + } - _ => return Err(( - Status::BadRequest, - Json(OAuthErrorResponse { error: "unsupported_grant_type".to_string() }) - )) + _ => { + return Err(( + Status::BadRequest, + Json(OAuthErrorResponse { + error: "unsupported_grant_type".to_string(), + }), + )); + } }; - let token = create_token(pool.inner(), pid, AUTH_TOKEN, None).await; + let token = create_token(pool.inner(), pid, AUTH_TOKEN, None, None).await; Ok(Json(OAuthTokenResponse { access_token: token, diff --git a/src/nnid/oauth/generate_token.rs b/src/nnid/oauth/generate_token.rs index a239ab3..0f16371 100644 --- a/src/nnid/oauth/generate_token.rs +++ b/src/nnid/oauth/generate_token.rs @@ -1,50 +1,43 @@ #![allow(unused)] -use rocket::{post, FromForm, State}; -use rocket::form::Form; -use serde::{Serialize}; +use crate::Pool; use crate::account::account::{Auth, DeviceCert, User, link_certificate_to_pid}; use crate::error::{Error, Errors}; use crate::nnid::agreements::{CFIP, EVIL_AGREEMENT_THING}; -use crate::nnid::oauth::generate_token::token_type::{AUTH_REFRESH_TOKEN, AUTH_TOKEN}; use crate::nnid::oauth::TokenData; -use crate::Pool; +use crate::nnid::oauth::generate_token::token_type::{AUTH_REFRESH_TOKEN, AUTH_TOKEN}; use crate::xml::Xml; +use rocket::form::Form; +use rocket::{FromForm, State, post}; +use serde::Serialize; -pub mod token_type{ +pub mod token_type { pub const AUTH_REFRESH_TOKEN: i32 = 1; pub const AUTH_TOKEN: i32 = 0; pub const NEX_TOKEN: i32 = 2; } -const ACCOUNT_ID_OR_PASSWORD_ERRORS: Errors = Errors{ - error: &[ - Error{ - code: "0106", - message: "Invalid account ID or password" - } - ] +const ACCOUNT_ID_OR_PASSWORD_ERRORS: Errors = Errors { + error: &[Error { + code: "0106", + message: "Invalid account ID or password", + }], }; -const ACCOUNT_BANNED_ERRORS: Errors = Errors{ - error: &[ - Error{ - code: "0108", - message: "Account banned from server" - } - ] +const ACCOUNT_BANNED_ERRORS: Errors = Errors { + error: &[Error { + code: "0108", + message: "Account banned from server", + }], }; - -const REREAD_EULA_EXTRABANNED_ERRORS: Errors = Errors{ - error: &[ - Error{ - code: "0109", - message: "REREAD THE EULA LOL" - } - ] +const REREAD_EULA_EXTRABANNED_ERRORS: Errors = Errors { + error: &[Error { + code: "0109", + message: "REREAD THE EULA LOL", + }], }; #[derive(FromForm)] -pub struct TokenRequestData<'a>{ +pub struct TokenRequestData<'a> { grant_type: &'a str, user_id: &'a str, password: &'a str, @@ -55,17 +48,27 @@ pub struct TokenRequestData<'a>{ pub struct TokenReturnData { token: String, refresh_token: String, - expires_in: i32 + expires_in: i32, } -pub async fn create_token(pool: &Pool, pid: i32, token_type: i32, title_id: Option<&str>) -> String{ +pub async fn create_token( + pool: &Pool, + pid: i32, + token_type: i32, + title_id: Option<&str>, + cert_hash: Option<&[u8]>, +) -> String { let data = sqlx::query!( - "insert into tokens (token_type, pid, title_id) - values ($1, $2, $3) returning token_id, random", - token_type, pid, title_id - ) - .fetch_one(pool) - .await.unwrap(); + "insert into tokens (token_type, pid, title_id, cert_hash) + values ($1, $2, $3, $4) returning token_id, random", + token_type, + pid, + title_id, + cert_hash + ) + .fetch_one(pool) + .await + .unwrap(); let token_id = data.token_id; let random = data.random; @@ -73,61 +76,79 @@ pub async fn create_token(pool: &Pool, pid: i32, token_type: i32, title_id: Opti let token = TokenData { token_id, random, - pid + pid, }; token.encode().to_string() } - impl TokenReturnData { - async fn new(pid: i32, pool: &Pool) -> Self{ - let token = create_token(pool, pid, AUTH_TOKEN, None).await; + async fn new(pid: i32, pool: &Pool) -> Self { + let token = create_token(pool, pid, AUTH_TOKEN, None, None).await; - let refresh_token = create_token(pool, pid, AUTH_REFRESH_TOKEN, None).await; + let refresh_token = create_token(pool, pid, AUTH_REFRESH_TOKEN, None, None).await; - Self{ + Self { token, refresh_token, - expires_in: 3600 + expires_in: 3600, + } + } + async fn new_with_cert(pid: i32, cert: Option<&[u8]>, pool: &Pool) -> Self { + let token = create_token(pool, pid, AUTH_TOKEN, None, None).await; + + let refresh_token = create_token(pool, pid, AUTH_REFRESH_TOKEN, None, None).await; + + Self { + token, + refresh_token, + expires_in: 3600, } } } #[derive(Serialize)] -#[serde(rename="OAuth20")] -pub struct TokenRequestReturnData{ - access_token: TokenReturnData +#[serde(rename = "OAuth20")] +pub struct TokenRequestReturnData { + access_token: TokenReturnData, } -#[post("/v1/api/oauth20/access_token/generate", data="")] -pub async fn generate_token(pool: &State, data: Form>, ip: CFIP, cert: DeviceCert) -> Result, Option>>{ +#[post("/v1/api/oauth20/access_token/generate", data = "")] +pub async fn generate_token( + pool: &State, + data: Form>, + ip: CFIP, + cert: DeviceCert, +) -> Result, Option>> { let pool = pool.inner(); - let user = User::get_by_username(data.user_id, pool).await + let user = User::get_by_username(data.user_id, pool) + .await .ok_or(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS))?; - if !user.verify_hashed_password(&data.password).is_some_and(|v| v){ + if !user + .verify_hashed_password(&data.password) + .is_some_and(|v| v) + { return Err(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS)); } - if user.account_level < 0{ + if user.account_level < 0 { if user.account_level == -2 { return Err(Some(REREAD_EULA_EXTRABANNED_ERRORS)); } - if user.account_level == -3{ + if user.account_level == -3 { EVIL_AGREEMENT_THING.write().await.insert(ip.0); return Err(Some(REREAD_EULA_EXTRABANNED_ERRORS)); } return Err(Some(ACCOUNT_BANNED_ERRORS)); } - + link_certificate_to_pid(&pool, &cert.0, user.pid).await?; - let access_token = TokenReturnData::new(user.pid, pool).await; + let access_token = + TokenReturnData::new_with_cert(user.pid, Some(&cert.0.hash()[..]), pool).await; - Ok(Xml(TokenRequestReturnData{ - access_token - })) -} \ No newline at end of file + Ok(Xml(TokenRequestReturnData { access_token })) +} diff --git a/src/nnid/provider.rs b/src/nnid/provider.rs index 850b47e..bbe41d5 100644 --- a/src/nnid/provider.rs +++ b/src/nnid/provider.rs @@ -4,10 +4,14 @@ use crate::error::{Error, Errors}; use crate::nnid::oauth::generate_token::create_token; use crate::nnid::oauth::generate_token::token_type::NEX_TOKEN; use crate::xml::Xml; +use log::{info, warn}; use nex_account::grpc::Pid; use reqwest::header::SERVER; -use rocket::{State, get}; +use rocket::http::Status; +use rocket::request::{FromRequest, Outcome, Request}; +use rocket::{State, async_trait, get}; use serde::Serialize; +use sqlx::query; use sqlx::types::ipnetwork::IpNetwork::V4; use std::net::Ipv4Addr; @@ -55,10 +59,52 @@ pub struct ServiceToken { token: String, } +pub async fn store_or_check_serial(pool: &Pool, serial: &str, cert_hash: [u8; 32]) -> bool { + let Ok(res) = query!( + "select serial from certificates where hash = $1", + &cert_hash[..] + ) + .fetch_one(pool) + .await + else { + warn!( + "user tried to access a route which is locked behind a console asociated token without a console token" + ); + return false; + }; + + let Some(stored_serial) = res.serial else { + query!( + "update certificates set serial = $1 where hash = $2", + serial, + &cert_hash[..] + ); + return true; + }; + + serial == stored_serial +} + +struct Serial(String); + +#[async_trait] +impl<'r> FromRequest<'r> for Serial { + type Error = Errors<'static>; + async fn from_request(request: &'r Request<'_>) -> Outcome { + let Some(header) = request.headers().get("X-Nintendo-Serial-Number").next() else { + warn!("serial number wasnt sent on request which expected a serial number"); + return Outcome::Error((Status::BadRequest, SERVER_ERROR)); + }; + + Outcome::Success(Self(header.to_owned())) + } +} + #[get("/v1/api/provider/service_token/@me")] pub async fn get_service_token( pool: &State, auth: Auth, + serial: Serial, ) -> Result, Option>> { // just gonna put this here as a side note for the future: // we could also be using key derivation to derive the nex token as if it were a key @@ -69,7 +115,17 @@ pub async fn get_service_token( let pool = pool.inner(); - let token = create_token(pool, auth.pid, NEX_TOKEN, None).await; + let Some(cert_hash) = auth.1 else { + info!("attempt to generate service token using non wii u/certificate token"); + return Err(Some(SERVER_ERROR)); + }; + + if !store_or_check_serial(pool, &serial.0, cert_hash).await { + info!("serial mismatched with certificate"); + return Err(Some(SERVER_ERROR)); + } + + let token = create_token(pool, auth.pid, NEX_TOKEN, None, Some(&cert_hash[..])).await; Ok(Xml(ServiceToken { token })) } From 9bf06fffd694f3349027ebbedbc0a85c1042e23d Mon Sep 17 00:00:00 2001 From: red binder Date: Thu, 16 Jul 2026 02:06:51 +0200 Subject: [PATCH 03/32] serial number and cert hash route --- ...d9d3e1961530ae963f9ab5442482d81228590.json | 28 +++++++++++++ ...c1c262351465b6f102e98912f67442a1f54d9.json | 21 ---------- ...604095f71324011e7093ec37af15da8c158f4.json | 14 ++++++- ...66fc2a97ac6e25a9733d1d20faf555c67abe1.json | 14 ++++++- ...8c840f66eec4e5458d3709c0e0f09afe51377.json | 15 +++++++ ...d15f9e4bafdfca12d2130a873d44a88dd435b.json | 23 ----------- ...4dc5cd7ae99fac34ae16023f9a6a701857c9.json} | 7 ++-- src/json_api/admin/bans.rs | 6 +-- src/json_api/users/console.rs | 40 +++++++++++++++++++ src/json_api/users/mod.rs | 3 +- src/main.rs | 1 + src/nnid/provider.rs | 9 +++-- 12 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 .sqlx/query-0ef5f08a84d6864291cb6125610d9d3e1961530ae963f9ab5442482d81228590.json delete mode 100644 .sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json create mode 100644 .sqlx/query-beeeea07993ba8daac1e43c985e8c840f66eec4e5458d3709c0e0f09afe51377.json delete mode 100644 .sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json rename .sqlx/{query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json => query-d8298703381f0e220bdbe122dbdc4dc5cd7ae99fac34ae16023f9a6a701857c9.json} (72%) create mode 100644 src/json_api/users/console.rs diff --git a/.sqlx/query-0ef5f08a84d6864291cb6125610d9d3e1961530ae963f9ab5442482d81228590.json b/.sqlx/query-0ef5f08a84d6864291cb6125610d9d3e1961530ae963f9ab5442482d81228590.json new file mode 100644 index 0000000..64e253e --- /dev/null +++ b/.sqlx/query-0ef5f08a84d6864291cb6125610d9d3e1961530ae963f9ab5442482d81228590.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "select serial from certificates where hash = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "serial", + "type_info": "Varchar", + "origin": { + "Table": { + "table": "certificates", + "name": "serial" + } + } + } + ], + "parameters": { + "Left": [ + "Bytea" + ] + }, + "nullable": [ + true + ] + }, + "hash": "0ef5f08a84d6864291cb6125610d9d3e1961530ae963f9ab5442482d81228590" +} diff --git a/.sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json b/.sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json deleted file mode 100644 index 9607895..0000000 --- a/.sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT nextval('pid_counter') as pid", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "pid", - "type_info": "Int8", - "origin": "Expression" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null - ] - }, - "hash": "3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9" -} diff --git a/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json b/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json index 88cc28a..96c9316 100644 --- a/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json +++ b/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json @@ -68,6 +68,17 @@ "name": "expires" } } + }, + { + "ordinal": 6, + "name": "cert_hash", + "type_info": "Bytea", + "origin": { + "Table": { + "table": "tokens", + "name": "cert_hash" + } + } } ], "parameters": { @@ -83,7 +94,8 @@ false, false, true, - false + false, + true ] }, "hash": "48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4" diff --git a/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json b/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json index b5366a8..fd3faba 100644 --- a/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json +++ b/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json @@ -68,6 +68,17 @@ "name": "expires" } } + }, + { + "ordinal": 6, + "name": "cert_hash", + "type_info": "Bytea", + "origin": { + "Table": { + "table": "tokens", + "name": "cert_hash" + } + } } ], "parameters": { @@ -83,7 +94,8 @@ false, false, true, - false + false, + true ] }, "hash": "9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1" diff --git a/.sqlx/query-beeeea07993ba8daac1e43c985e8c840f66eec4e5458d3709c0e0f09afe51377.json b/.sqlx/query-beeeea07993ba8daac1e43c985e8c840f66eec4e5458d3709c0e0f09afe51377.json new file mode 100644 index 0000000..9d62095 --- /dev/null +++ b/.sqlx/query-beeeea07993ba8daac1e43c985e8c840f66eec4e5458d3709c0e0f09afe51377.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "update certificates set serial = $1 where hash = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Bytea" + ] + }, + "nullable": [] + }, + "hash": "beeeea07993ba8daac1e43c985e8c840f66eec4e5458d3709c0e0f09afe51377" +} diff --git a/.sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json b/.sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json deleted file mode 100644 index 103f7d7..0000000 --- a/.sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(select 1 from users where pid = $1)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool", - "origin": "Expression" - } - ], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [ - null - ] - }, - "hash": "c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b" -} diff --git a/.sqlx/query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json b/.sqlx/query-d8298703381f0e220bdbe122dbdc4dc5cd7ae99fac34ae16023f9a6a701857c9.json similarity index 72% rename from .sqlx/query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json rename to .sqlx/query-d8298703381f0e220bdbe122dbdc4dc5cd7ae99fac34ae16023f9a6a701857c9.json index c190306..d7473a0 100644 --- a/.sqlx/query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json +++ b/.sqlx/query-d8298703381f0e220bdbe122dbdc4dc5cd7ae99fac34ae16023f9a6a701857c9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "insert into tokens (token_type, pid, title_id)\n values ($1, $2, $3) returning token_id, random", + "query": "insert into tokens (token_type, pid, title_id, cert_hash)\n values ($1, $2, $3, $4) returning token_id, random", "describe": { "columns": [ { @@ -30,7 +30,8 @@ "Left": [ "Int4", "Int4", - "Varchar" + "Varchar", + "Bytea" ] }, "nullable": [ @@ -38,5 +39,5 @@ false ] }, - "hash": "e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2" + "hash": "d8298703381f0e220bdbe122dbdc4dc5cd7ae99fac34ae16023f9a6a701857c9" } diff --git a/src/json_api/admin/bans.rs b/src/json_api/admin/bans.rs index 94ebef5..fa211c6 100644 --- a/src/json_api/admin/bans.rs +++ b/src/json_api/admin/bans.rs @@ -1,11 +1,7 @@ -use rocket::serde::json::Json; use rocket::{post, FromForm, State}; use rocket::form::Form; -use rocket::futures::TryFutureExt; use rocket::http::Status; use crate::account::account::Auth; -use crate::json_api::oauth::generate_token::TokenRequest; -use crate::nnid::people::{build_oauth_profile, GetOwnOAuthProfileData}; use crate::Pool; #[derive(FromForm)] @@ -21,7 +17,7 @@ pub async fn ban_user(pool: &State, auth: Auth, request: Form, auth: Auth) -> Result, (Status, &'static str)> { + let hash = auth.1.ok_or((Status::BadRequest, "token needs cert hash"))?; + + let row = sqlx::query!( + "select serial from certificates where hash = $1", + &hash[..] + ) + .fetch_one(pool.inner()) + .await + .map_err(|e| { + log::error!("failed to execute query: {:?}", e); + (Status::InternalServerError, "error in database query") + })?; + + let hex_hash = hex::encode(hash); + let serial = row.serial.ok_or((Status::BadRequest, "we had no serial"))?; + + Ok( + Json ( + ConsoleData { + serial, + cert_hash: hex_hash + } + ) + ) +} \ No newline at end of file diff --git a/src/json_api/users/mod.rs b/src/json_api/users/mod.rs index b284f7e..a71fca3 100644 --- a/src/json_api/users/mod.rs +++ b/src/json_api/users/mod.rs @@ -1,3 +1,4 @@ pub mod profile; pub mod mii; -pub mod delete; \ No newline at end of file +pub mod delete; +pub mod console; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 35a48df..1ad5ca5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,6 +119,7 @@ async fn launch() -> _ { json_api::oauth::authorize::authorize_page, json_api::oauth::authorize::authorize_submit, json_api::admin::bans::ban_user, + json_api::users::console::get_own_console, nnid::people::thing, // graphql::graphiql, // graphql::playground, diff --git a/src/nnid/provider.rs b/src/nnid/provider.rs index bbe41d5..316d003 100644 --- a/src/nnid/provider.rs +++ b/src/nnid/provider.rs @@ -6,7 +6,6 @@ use crate::nnid::oauth::generate_token::token_type::NEX_TOKEN; use crate::xml::Xml; use log::{info, warn}; use nex_account::grpc::Pid; -use reqwest::header::SERVER; use rocket::http::Status; use rocket::request::{FromRequest, Outcome, Request}; use rocket::{State, async_trait, get}; @@ -74,18 +73,20 @@ pub async fn store_or_check_serial(pool: &Pool, serial: &str, cert_hash: [u8; 32 }; let Some(stored_serial) = res.serial else { - query!( + let _row = query!( "update certificates set serial = $1 where hash = $2", serial, &cert_hash[..] - ); + ).execute(pool) + .await + .ok(); return true; }; serial == stored_serial } -struct Serial(String); +pub struct Serial(pub String); #[async_trait] impl<'r> FromRequest<'r> for Serial { From 19505b43484e6d586bfa32d141166521e12dfda7 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Thu, 16 Jul 2026 03:40:05 +0200 Subject: [PATCH 04/32] actually asociate cert hash with token --- src/nnid/oauth/generate_token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnid/oauth/generate_token.rs b/src/nnid/oauth/generate_token.rs index 0f16371..9383359 100644 --- a/src/nnid/oauth/generate_token.rs +++ b/src/nnid/oauth/generate_token.rs @@ -97,7 +97,7 @@ impl TokenReturnData { async fn new_with_cert(pid: i32, cert: Option<&[u8]>, pool: &Pool) -> Self { let token = create_token(pool, pid, AUTH_TOKEN, None, None).await; - let refresh_token = create_token(pool, pid, AUTH_REFRESH_TOKEN, None, None).await; + let refresh_token = create_token(pool, pid, AUTH_REFRESH_TOKEN, None, cert).await; Self { token, From 51602061472141fb383cc4da309b305c71ca235f Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Thu, 16 Jul 2026 03:54:06 +0200 Subject: [PATCH 05/32] actually actually asociate cert hash with token --- src/nnid/oauth/generate_token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnid/oauth/generate_token.rs b/src/nnid/oauth/generate_token.rs index 9383359..012627f 100644 --- a/src/nnid/oauth/generate_token.rs +++ b/src/nnid/oauth/generate_token.rs @@ -95,7 +95,7 @@ impl TokenReturnData { } } async fn new_with_cert(pid: i32, cert: Option<&[u8]>, pool: &Pool) -> Self { - let token = create_token(pool, pid, AUTH_TOKEN, None, None).await; + let token = create_token(pool, pid, AUTH_TOKEN, None, cert).await; let refresh_token = create_token(pool, pid, AUTH_REFRESH_TOKEN, None, cert).await; From 9037439d9eb7262db812f06157f2495904dfe9c1 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Sat, 18 Jul 2026 23:45:29 +0000 Subject: [PATCH 06/32] Update Rust crate serde to v1.0.229 --- Cargo.lock | 101 +++++++++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..315053b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,7 +307,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -318,7 +318,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -360,7 +360,7 @@ dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -514,7 +514,7 @@ dependencies = [ "owo-colors", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -604,7 +604,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1006,7 +1006,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.118", "unicode-xid", ] @@ -1018,7 +1018,7 @@ checksum = "362f47930db19fe7735f527e6595e4900316b893ebf6d48ad3d31be928d57dd6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1051,7 +1051,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1096,7 +1096,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1240,7 +1240,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2211,7 +2211,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.118", ] [[package]] @@ -2230,7 +2230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2285,7 +2285,7 @@ dependencies = [ "derive_more", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "url", ] @@ -2867,7 +2867,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2993,7 +2993,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3048,7 +3048,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3114,7 +3114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.118", ] [[package]] @@ -3161,7 +3161,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "version_check", "yansi", ] @@ -3193,7 +3193,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn", + "syn 2.0.118", "tempfile", ] @@ -3207,7 +3207,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3444,7 +3444,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3617,7 +3617,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn", + "syn 2.0.118", "unicode-xid", "version_check", ] @@ -3998,9 +3998,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -4008,22 +4008,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.0", ] [[package]] @@ -4328,7 +4328,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn", + "syn 2.0.118", ] [[package]] @@ -4351,7 +4351,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn", + "syn 2.0.118", "thiserror 2.0.18", "tokio", "url", @@ -4504,6 +4504,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -4521,7 +4532,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4593,7 +4604,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4604,7 +4615,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4698,7 +4709,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4824,7 +4835,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4849,7 +4860,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn", + "syn 2.0.118", "tempfile", "tonic-build", ] @@ -4923,7 +4934,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5225,7 +5236,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -5340,7 +5351,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5351,7 +5362,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5588,7 +5599,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "synstructure", ] @@ -5609,7 +5620,7 @@ checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5629,7 +5640,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "synstructure", ] @@ -5669,7 +5680,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] From 4621679bf5f3180964b4e36961f142942e6d15df Mon Sep 17 00:00:00 2001 From: Spacebot Date: Sun, 19 Jul 2026 19:45:35 +0000 Subject: [PATCH 07/32] Update Rust crate bytemuck to v1.25.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..8586d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -589,9 +589,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] From f2a5997dab30723c34dd33a5473a7ada1a66ad55 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Mon, 20 Jul 2026 06:15:34 +0000 Subject: [PATCH 08/32] Update Rust crate serde_json to v1.0.151 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..b48b3b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4028,9 +4028,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", From 08146a3604d4a6c99604be8a2999c1653c6d7379 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Mon, 20 Jul 2026 08:45:40 +0000 Subject: [PATCH 09/32] Update Rust crate time to v0.3.54 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..f9e6e47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4618,9 +4618,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", "libc", @@ -4640,9 +4640,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", From 44b380e6c1e0127c69b4facf6202e8ec5dbdaf67 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Mon, 20 Jul 2026 17:45:57 +0000 Subject: [PATCH 10/32] Update Rust crate tokio to v1.53.1 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..a29ebb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2166,7 +2166,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3303,7 +3303,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3697,7 +3697,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3754,7 +3754,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4555,7 +4555,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4675,9 +4675,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -5301,7 +5301,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] From 08a789bd643e0fe3fb9ef71853cf716b95f49ea7 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Tue, 28 Jul 2026 13:55:06 +0000 Subject: [PATCH 11/32] Update Rust crate dsa to 0.7.0 --- Cargo.lock | 96 ++++++++++++++++++------------------------------------ Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..1f0dc7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -908,6 +908,7 @@ dependencies = [ "hybrid-array", "num-traits", "rand_core 0.10.1", + "serdect", "subtle", "zeroize", ] @@ -933,6 +934,16 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint 0.7.5", + "rand_core 0.10.1", +] + [[package]] name = "ctutils" version = "0.4.2" @@ -1107,17 +1118,19 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dsa" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +checksum = "5c8573b60d07b5b5cecadc1f978c9b71d5504151e703cbb0439ee4ad301338f3" dependencies = [ - "digest 0.10.7", - "num-bigint-dig", - "num-traits", - "pkcs8 0.10.2", - "rfc6979 0.4.0", - "sha2 0.10.9", - "signature 2.2.0", + "crypto-bigint 0.7.5", + "crypto-common 0.2.2", + "crypto-primes", + "der 0.8.0", + "digest 0.11.3", + "pkcs8 0.11.0", + "rfc6979 0.6.0", + "sha2 0.11.0", + "signature 3.0.0", "zeroize", ] @@ -1256,7 +1269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1955,7 +1968,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -2166,7 +2179,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2330,9 +2343,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] [[package]] name = "lettre" @@ -2364,12 +2374,6 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - [[package]] name = "libsqlite3-sys" version = "0.37.0" @@ -2601,48 +2605,12 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.6", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3262,7 +3230,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -3301,9 +3269,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3697,7 +3665,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3754,7 +3722,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4555,7 +4523,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5301,7 +5269,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 42135b7..33d5270 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ ecdsa = { version = "0.16.9", features = ["pem", "std", "verifying"] } sha256 = "1.6.0" p256 = "0.14.0" k256 = "0.13.4" -dsa = "0.6.3" +dsa = "0.7.0" openssl = {version = "0.10.78", features = ["vendored"]} time = "0.3.47" hickory-resolver = { version = "0.24", features = ["tokio-runtime"] } From 9b7d7928a3bb4854902f98c19b6c6a324d42fcc5 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 11:16:02 +0000 Subject: [PATCH 12/32] Update Rust crate sentry to 0.49.0 --- Cargo.lock | 70 ++++++++++++++++++++++++++++++++---------------------- Cargo.toml | 2 +- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 693cca8..46294e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1955,7 +1955,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -2166,7 +2166,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3262,7 +3262,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -3301,9 +3301,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3697,7 +3697,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3754,7 +3754,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3876,9 +3876,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "sentry" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1975064d9f3d9d87a7c8f0371f7fac10d876906ca3e39faad72e61c263ff9b87" +checksum = "d91e4790d8c2f43a6645ee2cb12aac2db79221fddd035b94c8166b88ea094408" dependencies = [ "cfg_aliases", "httpdate", @@ -3889,6 +3889,7 @@ dependencies = [ "sentry-contexts", "sentry-core", "sentry-debug-images", + "sentry-log", "sentry-panic", "sentry-tracing", "tokio", @@ -3897,9 +3898,9 @@ dependencies = [ [[package]] name = "sentry-actix" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488b43adc03f07b01563ea078c33285c5a9bbbc34d07aaa483d82922db959637" +checksum = "5a12b26925e70eba6c346588650f8b346d9e78a83249970b706068456005cd61" dependencies = [ "actix-http", "actix-web", @@ -3910,9 +3911,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134f552b6b147f77aeee46ece875d67a8bfc1d56fe3860d78446ed4c25bb5f9f" +checksum = "326e106874a7ea90636f1ca42e2f7b912929d29307982cab37f81208c16cf043" dependencies = [ "backtrace", "regex", @@ -3921,9 +3922,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98482b938fb1f31ecd23506fb7f08b2ba20d60b9cc72581b1205a9acd31d32fa" +checksum = "3ab62ebc8076326e509c71a7424d2aaeff63fba9bc4e5b55074cd33f356c759f" dependencies = [ "hostname", "libc", @@ -3935,9 +3936,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cff96247f4dc36867511d108709e703eb354143e7893319d763d2dd1edb4f48" +checksum = "2f9efafefbb78d7e02cb06c10aec08b77e7500428389eabbf6d5a668325265e8" dependencies = [ "rand 0.9.4", "sentry-types", @@ -3948,19 +3949,30 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b603a51b083141062a142d73e36391b14244b4bd0bfe28bcd80e8327bb1d7698" +checksum = "cee1e7c19e715ae9538114bbd09c2442689a481e2adbc8245c9e06b20d2bd2be" dependencies = [ "findshlibs", "sentry-core", ] [[package]] -name = "sentry-panic" -version = "0.48.3" +name = "sentry-log" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de3f4a1fc77d4c57e8bacd8ef064c26aaa88ea5b2541a761d37c7c3703b9a9" +checksum = "ba08897af468a109afafba1f00c25d40070ef76309759833cccb83151a5be73d" +dependencies = [ + "bitflags", + "log", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fc536a3e1fc68d626ae8dd18b628cafd474d9d36fea74e4bbb4d038877f003" dependencies = [ "sentry-backtrace", "sentry-core", @@ -3968,9 +3980,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc59b27dae3bb495e37e6d62d252214840a2e7e1875531ee9fa2953df8985537" +checksum = "96be2253fe14b3fa10c1ba047af2d3e58ce85c8cb297bbd19d6fa81b604e7877" dependencies = [ "bitflags", "sentry-backtrace", @@ -3981,9 +3993,9 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.48.3" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d7e78132ccfb4a1c777b959fb2944d1f6d5145bffe6be2a98ee6b25d244f72c" +checksum = "f967748632cd4c5405dbed45426b27b407c0e53ac59b7df1c163e7f5aa57a77c" dependencies = [ "debugid", "hex", @@ -4555,7 +4567,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5301,7 +5313,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 42135b7..b629b91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ md-5 = "0.11.0" cbc = "0.1.2" crc32fast = "1.4.2" gxhash = "3.4.1" -sentry = "0.48.0" +sentry = "0.49.0" rocket_cors = "0.6.0" juniper = { version = "0.17.0", features = ["chrono"] } From 02a4b2b50420633b999bcc9e492f80c8fd431ff2 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 14:00:54 +0000 Subject: [PATCH 13/32] Update Rust crate binrw to v0.15.2 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 394b1a7..77ba662 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -495,9 +495,9 @@ checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" [[package]] name = "binrw" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53195f985e88ab94d1cc87e80049dd2929fd39e4a772c5ae96a7e5c4aad3642" +checksum = "6ad120d555272286c1017d25165ab8bd74806f13fc85b258484ec7e4ce75458f" dependencies = [ "array-init", "binrw_derive", @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "binrw_derive" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5910da05ee556b789032c8ff5a61fb99239580aa3fd0bfaa8f4d094b2aee00ad" +checksum = "6df92e0e9baae4dc82c7bad7715ca40c0a5c71539057bf2ea04a5c29c980410b" dependencies = [ "either", "owo-colors", From 579ba418c18e9d387f8b77c0e5364f70e891cb5b Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 14:01:04 +0000 Subject: [PATCH 14/32] Update Rust crate thiserror to v2.0.19 --- Cargo.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 394b1a7..4e3f082 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,7 +44,7 @@ dependencies = [ "sha2 0.11.0", "sha256", "sqlx", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "tokio", ] @@ -2209,7 +2209,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", "windows-link", ] @@ -2568,7 +2568,7 @@ dependencies = [ "simplelog", "spbr-common", "sqlx", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tonic", "tonic-prost", @@ -3231,7 +3231,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.6.4", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tracing", "web-time", @@ -3254,7 +3254,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror 2.0.19", "tinyvec", "tracing", "web-time", @@ -3970,7 +3970,7 @@ dependencies = [ "rand 0.9.4", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "url", "uuid", @@ -4216,7 +4216,7 @@ dependencies = [ "hybrid-array", "md-5", "rand 0.10.2", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -4291,7 +4291,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-stream", "tracing", @@ -4332,7 +4332,7 @@ dependencies = [ "sqlx-postgres", "sqlx-sqlite", "syn 2.0.118", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "url", ] @@ -4360,7 +4360,7 @@ dependencies = [ "sha1", "sha2 0.11.0", "sqlx-core", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", ] @@ -4396,7 +4396,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", "whoami", ] @@ -4421,7 +4421,7 @@ dependencies = [ "percent-encoding", "serde", "sqlx-core", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", "url", ] @@ -4569,11 +4569,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.19", ] [[package]] @@ -4589,13 +4589,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.0", ] [[package]] From e7c94f30cd7810742108ffb3189a725dfecb8255 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 14:01:10 +0000 Subject: [PATCH 15/32] Update Rust crate base64 to 0.23.0 --- Cargo.lock | 32 +++++++++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 394b1a7..8ae7f65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "aes 0.8.4", "argon2", - "base64", + "base64 0.23.0", "bcrypt", "binrw", "bytemuck", @@ -468,6 +468,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9" + [[package]] name = "base64ct" version = "1.8.3" @@ -480,7 +486,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f3c067aa24dd4ed5c79cf222a38f260c8f23d3b82a062fba3f28c6fe563b753" dependencies = [ - "base64", + "base64 0.22.1", "blowfish", "getrandom 0.4.3", "subtle", @@ -1225,7 +1231,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6" dependencies = [ - "base64", + "base64 0.22.1", "memchr", ] @@ -1957,7 +1963,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -2350,7 +2356,7 @@ version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0da65617f6cb926332d039cb578aad56178da86e128db6a1b09f4c94fa5b3349" dependencies = [ - "base64", + "base64 0.22.1", "email-encoding", "email_address", "fastrand", @@ -2559,7 +2565,7 @@ version = "0.2.4" source = "sparse+https://crates.spbr.net/api/v1/crates/" checksum = "6f1cfe935e71c764dfd6bad9b23372396f785408536c0a23fba379ef405117d3" dependencies = [ - "base64", + "base64 0.22.1", "bytemuck", "hmac 0.13.0", "log", @@ -3456,7 +3462,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", @@ -4210,7 +4216,7 @@ source = "sparse+https://crates.spbr.net/api/v1/crates/" checksum = "04f4e4710d23bb730a95d00b853e304a4c1c9e86276792cd32a92057e9833452" dependencies = [ "aes 0.9.1", - "base64", + "base64 0.22.1", "bytemuck", "hmac 0.13.0", "hybrid-array", @@ -4267,7 +4273,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "cfg-if", "chrono", @@ -4371,7 +4377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "chrono", @@ -4785,7 +4791,7 @@ checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum", - "base64", + "base64 0.22.1", "bytes", "h2 0.4.15", "http 1.4.2", @@ -5063,7 +5069,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ - "base64", + "base64 0.22.1", "der 0.8.0", "log", "native-tls", @@ -5080,7 +5086,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ - "base64", + "base64 0.22.1", "http 1.4.2", "httparse", "log", diff --git a/Cargo.toml b/Cargo.toml index c15fcd1..74d75b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ chrono = { version = "0.4.39", features = ["serde"] } argon2 = "0.5.3" sha2 = "0.11.0" bytemuck = { version = "1.21.0", features = ["derive"] } -base64 = "0.22.1" +base64 = "0.23.0" hex = "0.4.3" thiserror = "2.0.11" bcrypt = "0.19.0" From 68b5f2251d07e6e9527ea706a01d99a89364fc4b Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 14:01:24 +0000 Subject: [PATCH 16/32] Update Rust crate k256 to 0.14.0 --- Cargo.lock | 53 +++++++++++++++++------------------------------------ Cargo.toml | 2 +- 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 394b1a7..f9d7798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ "gxhash", "hex", "hickory-resolver", - "hmac 0.13.0", + "hmac", "juniper", "juniper_rocket", "k256", @@ -1128,7 +1128,7 @@ dependencies = [ "der 0.8.0", "digest 0.11.3", "pkcs8 0.11.0", - "rfc6979 0.6.0", + "rfc6979", "sha2 0.11.0", "signature 3.0.0", "zeroize", @@ -1149,7 +1149,6 @@ dependencies = [ "der 0.7.10", "digest 0.10.7", "elliptic-curve 0.13.8", - "rfc6979 0.4.0", "signature 2.2.0", "spki 0.7.3", ] @@ -1163,7 +1162,7 @@ dependencies = [ "der 0.8.0", "digest 0.11.3", "elliptic-curve 0.14.1", - "rfc6979 0.6.0", + "rfc6979", "signature 3.0.0", "spki 0.8.0", "zeroize", @@ -1750,16 +1749,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ - "hmac 0.13.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", + "hmac", ] [[package]] @@ -2320,16 +2310,17 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +checksum = "93f50113171a713f4a4231ef82eb26703607139b35dcb56241f0ceab2ae1f7d8" dependencies = [ - "cfg-if", - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "once_cell", - "sha2 0.10.9", - "signature 2.2.0", + "cpubits", + "ecdsa 0.17.0", + "elliptic-curve 0.14.1", + "primeorder", + "sha2 0.11.0", + "signature 3.0.0", + "wnaf", ] [[package]] @@ -2561,7 +2552,7 @@ checksum = "6f1cfe935e71c764dfd6bad9b23372396f785408536c0a23fba379ef405117d3" dependencies = [ "base64", "bytemuck", - "hmac 0.13.0", + "hmac", "log", "md-5", "prost", @@ -3501,16 +3492,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac 0.12.1", - "subtle", -] - [[package]] name = "rfc6979" version = "0.6.0" @@ -3518,7 +3499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" dependencies = [ "crypto-bigint 0.7.5", - "hmac 0.13.0", + "hmac", ] [[package]] @@ -4212,7 +4193,7 @@ dependencies = [ "aes 0.9.1", "base64", "bytemuck", - "hmac 0.13.0", + "hmac", "hybrid-array", "md-5", "rand 0.10.2", @@ -4383,7 +4364,7 @@ dependencies = [ "futures-util", "hex", "hkdf", - "hmac 0.13.0", + "hmac", "ipnetwork", "itoa", "log", diff --git a/Cargo.toml b/Cargo.toml index c15fcd1..0fb1259 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ binrw = "0.15.1" ecdsa = { version = "0.16.9", features = ["pem", "std", "verifying"] } sha256 = "1.6.0" p256 = "0.14.0" -k256 = "0.13.4" +k256 = "0.14.0" dsa = "0.7.0" openssl = {version = "0.10.78", features = ["vendored"]} time = "0.3.47" From 9951c60327ac42898aa8cb8a20c9b5a4545dacc8 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 14:30:30 +0000 Subject: [PATCH 17/32] Update Rust crate bcrypt to v0.19.3 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 066f478..62a3ad1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,11 +482,11 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bcrypt" -version = "0.19.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f3c067aa24dd4ed5c79cf222a38f260c8f23d3b82a062fba3f28c6fe563b753" +checksum = "a0cd0bd35a28836d528d2b58ad499bc3c5641d59379421b1be9eeb0c2f2b912a" dependencies = [ - "base64 0.22.1", + "base64 0.23.0", "blowfish", "getrandom 0.4.3", "subtle", @@ -4530,7 +4530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", From d76175fbeaee961f81841dabcceeeab7436a6131 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Wed, 29 Jul 2026 15:00:37 +0000 Subject: [PATCH 18/32] Update Rust crate nex-account to v0.2.5 --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62a3ad1..01022cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,7 +1274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1964,7 +1964,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -2175,7 +2175,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2552,11 +2552,11 @@ dependencies = [ [[package]] name = "nex-account" -version = "0.2.4" +version = "0.2.5" source = "sparse+https://crates.spbr.net/api/v1/crates/" -checksum = "6f1cfe935e71c764dfd6bad9b23372396f785408536c0a23fba379ef405117d3" +checksum = "7cb5867c4f4771dd7125dce1f911753cc9291b65cd7908c0e7ca182e7b2a57a3" dependencies = [ - "base64 0.22.1", + "base64 0.23.0", "bytemuck", "hmac", "log", @@ -3227,7 +3227,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.19", "tokio", "tracing", @@ -3266,9 +3266,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3652,7 +3652,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3709,7 +3709,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4530,10 +4530,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5279,7 +5279,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] From 66e8e689af026c53edcf65f9969da46f769d4b08 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Sat, 1 Aug 2026 08:30:31 +0000 Subject: [PATCH 19/32] Update Rust crate time to v0.3.55 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01022cc..cf200d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4596,9 +4596,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.54" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "libc", From e3489f4eaaa1e6f1825935f1bf1ac35363b14897 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Mon, 3 Aug 2026 11:45:27 +0000 Subject: [PATCH 20/32] Update Rust crate lettre to v0.11.23 --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01022cc..f807ab9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,7 +1274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2175,7 +2175,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2343,11 +2343,11 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lettre" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da65617f6cb926332d039cb578aad56178da86e128db6a1b09f4c94fa5b3349" +checksum = "f2c646bd5cc763b1087b15493e29a64be6147ba8f19342004fa52048ee596eae" dependencies = [ - "base64 0.22.1", + "base64 0.23.0", "email-encoding", "email_address", "fastrand", @@ -3268,7 +3268,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3652,7 +3652,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3709,7 +3709,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4533,7 +4533,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5279,7 +5279,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] From b814887461c2d8db6c53c39e0b21427e33b1a497 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Mon, 3 Aug 2026 15:00:41 +0000 Subject: [PATCH 21/32] Update Rust crate sentry to v0.49.1 --- Cargo.lock | 60 +++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01022cc..e8c08df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,7 +1274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1964,7 +1964,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -2175,7 +2175,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3227,7 +3227,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.19", "tokio", "tracing", @@ -3266,9 +3266,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3652,7 +3652,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3709,7 +3709,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3831,9 +3831,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "sentry" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91e4790d8c2f43a6645ee2cb12aac2db79221fddd035b94c8166b88ea094408" +checksum = "63207365db50cb817402f0ec8097d6dad3d644ef3fffd6ba30c75b3077e514da" dependencies = [ "cfg_aliases", "httpdate", @@ -3853,9 +3853,9 @@ dependencies = [ [[package]] name = "sentry-actix" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a12b26925e70eba6c346588650f8b346d9e78a83249970b706068456005cd61" +checksum = "4a0eb1ed18478fb48db007aeaa672a3de176055357feb768eee0c3471802d0ce" dependencies = [ "actix-http", "actix-web", @@ -3866,9 +3866,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "326e106874a7ea90636f1ca42e2f7b912929d29307982cab37f81208c16cf043" +checksum = "e3bcc2497c2327998146207b7600599ef7592233920f4d4e1d41ddeeba0e4210" dependencies = [ "backtrace", "regex", @@ -3877,9 +3877,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab62ebc8076326e509c71a7424d2aaeff63fba9bc4e5b55074cd33f356c759f" +checksum = "1cb04cba225b38f59d08f9e3c29ab7259d9cc94fbb2c46d613a51cb0a4a7ee05" dependencies = [ "hostname", "libc", @@ -3891,9 +3891,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9efafefbb78d7e02cb06c10aec08b77e7500428389eabbf6d5a668325265e8" +checksum = "48759bc392fb5e3b36b4efcb485f51074c0ba8479711cd5c8cf79e86f9f75d41" dependencies = [ "rand 0.9.4", "sentry-types", @@ -3904,9 +3904,9 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee1e7c19e715ae9538114bbd09c2442689a481e2adbc8245c9e06b20d2bd2be" +checksum = "0a5bd325059b70b21ca6e42b0f2409821272dddc1cf37923de37269af55389b5" dependencies = [ "findshlibs", "sentry-core", @@ -3914,9 +3914,9 @@ dependencies = [ [[package]] name = "sentry-log" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba08897af468a109afafba1f00c25d40070ef76309759833cccb83151a5be73d" +checksum = "f9553a2f0f75bc8550137ebc753e8d2161af63ff780ca59983935f8df8f01655" dependencies = [ "bitflags", "log", @@ -3925,9 +3925,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fc536a3e1fc68d626ae8dd18b628cafd474d9d36fea74e4bbb4d038877f003" +checksum = "a685d22f672b1562ebeff3f2affceb5960595648cc936dae73da3e1d6a55fbd1" dependencies = [ "sentry-backtrace", "sentry-core", @@ -3935,9 +3935,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96be2253fe14b3fa10c1ba047af2d3e58ce85c8cb297bbd19d6fa81b604e7877" +checksum = "8aa7d8e0db4cccddbac01ddabd5344ad03b7c2f954b3ff850cecb1d9cf5d4758" dependencies = [ "bitflags", "sentry-backtrace", @@ -3948,9 +3948,9 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f967748632cd4c5405dbed45426b27b407c0e53ac59b7df1c163e7f5aa57a77c" +checksum = "fab146d15a30ab4897a95fd15d6a038b8d7fbf2661c5f8b13738ce8df7a095b7" dependencies = [ "debugid", "hex", @@ -4533,7 +4533,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5279,7 +5279,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] From 282b1c34cf5bbcaa85233eaaaa060b4f43349144 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Tue, 4 Aug 2026 13:00:36 +0000 Subject: [PATCH 22/32] Update Rust crate base64 to v0.23.1 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01022cc..20be14d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "aes 0.8.4", "argon2", - "base64 0.23.0", + "base64 0.23.1", "bcrypt", "binrw", "bytemuck", @@ -470,9 +470,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "base64ct" @@ -486,7 +486,7 @@ version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0cd0bd35a28836d528d2b58ad499bc3c5641d59379421b1be9eeb0c2f2b912a" dependencies = [ - "base64 0.23.0", + "base64 0.23.1", "blowfish", "getrandom 0.4.3", "subtle", @@ -2556,7 +2556,7 @@ version = "0.2.5" source = "sparse+https://crates.spbr.net/api/v1/crates/" checksum = "7cb5867c4f4771dd7125dce1f911753cc9291b65cd7908c0e7ca182e7b2a57a3" dependencies = [ - "base64 0.23.0", + "base64 0.23.1", "bytemuck", "hmac", "log", From b934df85314962421db36ecc48bd7ffdb5095713 Mon Sep 17 00:00:00 2001 From: redbinder0526 Date: Wed, 5 Aug 2026 14:31:15 +0200 Subject: [PATCH 23/32] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 832368b..f435181 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ lettre = "0.11.15" rand = { version = "0.10.0", features = ["std"] } reqwest = "0.13.0" binrw = "0.15.1" -ecdsa = { version = "0.17.0", features = ["pem", "std", "verifying"] } +ecdsa = { version = "0.17.0", features = ["pem", "std", "algorithm", "digest"] } sha256 = "1.6.0" p256 = "0.13.2" k256 = "0.13.4" From e00913e7a35efdfe9436d6c7d9a00b58089b5494 Mon Sep 17 00:00:00 2001 From: Spacebot Date: Sat, 8 Aug 2026 13:16:13 +0000 Subject: [PATCH 24/32] Update Rust crate thiserror to v2.0.20 --- Cargo.lock | 258 ++++++++++++----------------------------------------- 1 file changed, 57 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c44e7f..2864314 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ dependencies = [ "crc32fast", "dotenvy", "dsa", - "ecdsa 0.16.9", + "ecdsa", "gxhash", "hex", "hickory-resolver", @@ -44,7 +44,7 @@ dependencies = [ "sha2 0.11.0", "sha256", "sqlx", - "thiserror 2.0.19", + "thiserror 2.0.20", "time", "tokio", ] @@ -450,12 +450,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base16ct" version = "1.0.0" @@ -769,12 +763,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "const-oid" version = "0.10.2" @@ -890,18 +878,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.7.5" @@ -946,7 +922,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" dependencies = [ - "crypto-bigint 0.7.5", + "crypto-bigint", "rand_core 0.10.1", ] @@ -976,25 +952,14 @@ dependencies = [ "uuid", ] -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid 0.9.6", - "pem-rfc7468 0.7.0", - "zeroize", -] - [[package]] name = "der" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ - "const-oid 0.10.2", - "pem-rfc7468 1.0.0", + "const-oid", + "pem-rfc7468", "zeroize", ] @@ -1078,7 +1043,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid 0.9.6", "crypto-common 0.1.7", "subtle", ] @@ -1090,7 +1054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.1", - "const-oid 0.10.2", + "const-oid", "crypto-common 0.2.2", "ctutils", ] @@ -1128,15 +1092,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8573b60d07b5b5cecadc1f978c9b71d5504151e703cbb0439ee4ad301338f3" dependencies = [ - "crypto-bigint 0.7.5", + "crypto-bigint", "crypto-common 0.2.2", "crypto-primes", - "der 0.8.0", + "der", "digest 0.11.3", - "pkcs8 0.11.0", + "pkcs8", "rfc6979", "sha2 0.11.0", - "signature 3.0.0", + "signature", "zeroize", ] @@ -1146,31 +1110,18 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der 0.7.10", - "digest 0.10.7", - "elliptic-curve 0.13.8", - "signature 2.2.0", - "spki 0.7.3", -] - [[package]] name = "ecdsa" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0" dependencies = [ - "der 0.8.0", + "der", "digest 0.11.3", - "elliptic-curve 0.14.1", + "elliptic-curve", "rfc6979", - "signature 3.0.0", - "spki 0.8.0", + "signature", + "spki", "zeroize", ] @@ -1183,43 +1134,23 @@ dependencies = [ "serde", ] -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", - "digest 0.10.7", - "ff 0.13.1", - "generic-array", - "group 0.13.0", - "pem-rfc7468 0.7.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.3", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65" dependencies = [ - "base16ct 1.0.0", - "crypto-bigint 0.7.5", + "base16ct", + "crypto-bigint", "crypto-common 0.2.2", "digest 0.11.3", - "ff 0.14.0", - "group 0.14.0", + "ff", + "group", "hybrid-array", - "pem-rfc7468 1.0.0", - "pkcs8 0.11.0", + "pem-rfc7468", + "pkcs8", "rand_core 0.10.1", - "sec1 0.8.1", + "sec1", "subtle", "zeroize", ] @@ -1304,16 +1235,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "ff" version = "0.14.0" @@ -1528,7 +1449,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1582,24 +1502,13 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff 0.13.1", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "group" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" dependencies = [ - "ff 0.14.0", + "ff", "rand_core 0.10.1", "subtle", ] @@ -2205,7 +2114,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.19", + "thiserror 2.0.20", "walkdir", "windows-link", ] @@ -2321,11 +2230,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f50113171a713f4a4231ef82eb26703607139b35dcb56241f0ceab2ae1f7d8" dependencies = [ "cpubits", - "ecdsa 0.17.0", - "elliptic-curve 0.14.1", + "ecdsa", + "elliptic-curve", "primeorder", "sha2 0.11.0", - "signature 3.0.0", + "signature", "wnaf", ] @@ -2347,7 +2256,7 @@ version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2c646bd5cc763b1087b15493e29a64be6147ba8f19342004fa52048ee596eae" dependencies = [ - "base64 0.23.0", + "base64 0.23.1", "email-encoding", "email_address", "fastrand", @@ -2565,7 +2474,7 @@ dependencies = [ "simplelog", "spbr-common", "sqlx", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tonic", "tonic-prost", @@ -2891,8 +2800,8 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a" dependencies = [ - "ecdsa 0.17.0", - "elliptic-curve 0.14.1", + "ecdsa", + "elliptic-curve", "primefield", "primeorder", "sha2 0.11.0", @@ -2961,15 +2870,6 @@ dependencies = [ "syn 2.0.118", ] -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "pem-rfc7468" version = "1.0.0" @@ -3022,24 +2922,14 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.10", - "spki 0.7.3", -] - [[package]] name = "pkcs8" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" dependencies = [ - "der 0.8.0", - "spki 0.8.0", + "der", + "spki", ] [[package]] @@ -3088,9 +2978,9 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" dependencies = [ - "crypto-bigint 0.7.5", + "crypto-bigint", "crypto-common 0.2.2", - "ff 0.14.0", + "ff", "rand_core 0.10.1", "subtle", "zeroize", @@ -3102,7 +2992,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06" dependencies = [ - "elliptic-curve 0.14.1", + "elliptic-curve", "once_cell", "primefield", "serdect", @@ -3228,7 +3118,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.6.4", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -3251,7 +3141,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.19", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -3504,7 +3394,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" dependencies = [ - "crypto-bigint 0.7.5", + "crypto-bigint", "hmac", ] @@ -3772,29 +3662,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.10", - "generic-array", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" dependencies = [ - "base16ct 1.0.0", + "base16ct", "ctutils", - "der 0.8.0", + "der", "hybrid-array", "subtle", "zeroize", @@ -3957,7 +3833,7 @@ dependencies = [ "rand 0.9.4", "serde", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", "time", "url", "uuid", @@ -4033,7 +3909,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" dependencies = [ - "base16ct 1.0.0", + "base16ct", "serde", ] @@ -4108,16 +3984,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - [[package]] name = "signature" version = "3.0.0" @@ -4203,7 +4069,7 @@ dependencies = [ "hybrid-array", "md-5", "rand 0.10.2", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -4215,16 +4081,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der 0.7.10", -] - [[package]] name = "spki" version = "0.8.0" @@ -4232,7 +4088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" dependencies = [ "base64ct", - "der 0.8.0", + "der", ] [[package]] @@ -4278,7 +4134,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "smallvec", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tokio-stream", "tracing", @@ -4319,7 +4175,7 @@ dependencies = [ "sqlx-postgres", "sqlx-sqlite", "syn 2.0.118", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "url", ] @@ -4347,7 +4203,7 @@ dependencies = [ "sha1", "sha2 0.11.0", "sqlx-core", - "thiserror 2.0.19", + "thiserror 2.0.20", "tracing", ] @@ -4383,7 +4239,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.19", + "thiserror 2.0.20", "tracing", "whoami", ] @@ -4408,7 +4264,7 @@ dependencies = [ "percent-encoding", "serde", "sqlx-core", - "thiserror 2.0.19", + "thiserror 2.0.20", "tracing", "url", ] @@ -4556,11 +4412,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -4576,9 +4432,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", @@ -5051,7 +4907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ "base64 0.22.1", - "der 0.8.0", + "der", "log", "native-tls", "percent-encoding", @@ -5527,8 +5383,8 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1" dependencies = [ - "ff 0.14.0", - "group 0.14.0", + "ff", + "group", "hybrid-array", ] From 8c01e2d5fd12e180116e98b674e555ff9b6d7619 Mon Sep 17 00:00:00 2001 From: red binder Date: Sat, 8 Aug 2026 21:11:30 +0200 Subject: [PATCH 25/32] log redirect uri --- src/json_api/oauth/authorize.rs | 3 ++- src/json_api/oauth/generate_token.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/json_api/oauth/authorize.rs b/src/json_api/oauth/authorize.rs index 07be2e9..6ade152 100644 --- a/src/json_api/oauth/authorize.rs +++ b/src/json_api/oauth/authorize.rs @@ -65,12 +65,13 @@ pub async fn authorize_submit( let expires_at = Utc::now().naive_utc() + Duration::minutes(5); if let Err(db_err) = sqlx::query( - "INSERT INTO oauth_auth_codes (code, pid, redirect_uri, expires_at) VALUES ($1, $2, $3, $4)" + "INSERT INTO oauth_auth_codes (code, pid, redirect_uri, expires_at, client_id) VALUES ($1, $2, $3, $4, $5)" ) .bind(&secure_auth_code) .bind(pid) .bind(form_data.redirect_uri) .bind(expires_at) + .bind(form_data.client_id) .execute(pool.inner()) .await { eprintln!("failed to save auth code: {:?}", db_err); diff --git a/src/json_api/oauth/generate_token.rs b/src/json_api/oauth/generate_token.rs index 767e374..091a99f 100644 --- a/src/json_api/oauth/generate_token.rs +++ b/src/json_api/oauth/generate_token.rs @@ -33,8 +33,6 @@ pub struct TokenRequest<'r> { } pub fn verify_nintendo_password(pid: i32, text_password: &str, db_bcrypt_hash: &str) -> bool { - // maple: binder, there's already a function for this, why duplicate code? - // binder: dear maple, with this function i can just hand it a pid, the password from the user and the db_bcrypt_hash and it gives me a true/false value. let mut sha = Sha256::new(); sha.update(bytes_of(&pid)); sha.update(&[0x02, 0x65, 0x43, 0x46]); @@ -54,6 +52,8 @@ pub async fn generate_token( pool: &State, form_data: Form>, ) -> Result, (Status, Json)> { + println!("redirect URI is: {:?}", form_data.redirect_uri); + match form_data.client_id { Some("account") | Some("splatnet") => { let cl_secret: String = CLIENT_SECRET.clone(); From d7c226b5a1f234a48d239dffa4912e53b8a4e843 Mon Sep 17 00:00:00 2001 From: red binder Date: Sun, 9 Aug 2026 14:52:45 +0200 Subject: [PATCH 26/32] add eula versioning --- res/agreement/EVIL.xml | 0 src/account/account.rs | 55 +++++++++++++++ src/nnid/agreements.rs | 111 ++++++++++++++++++------------- src/nnid/oauth/generate_token.rs | 25 ++++--- src/nnid/people.rs | 38 ++++++++++- 5 files changed, 168 insertions(+), 61 deletions(-) delete mode 100644 res/agreement/EVIL.xml diff --git a/res/agreement/EVIL.xml b/res/agreement/EVIL.xml deleted file mode 100644 index e69de29..0000000 diff --git a/src/account/account.rs b/src/account/account.rs index 41c285a..5dd5fe0 100644 --- a/src/account/account.rs +++ b/src/account/account.rs @@ -64,6 +64,7 @@ pub struct User { pub updated: NaiveDateTime, pub nex_password: String, pub verification_code: Option, + pub eula_version: i32, } #[derive(sqlx::FromRow)] @@ -170,6 +171,59 @@ pub async fn read_basic_auth_token( } } +pub async fn read_hashed_basic_auth_token( + connection: &Pool, + token: &str, +) -> Option<(User, Option<[u8; 32]>)> { + let data = match BASE64_STANDARD.decode(token) { + Ok(d) => d, + Err(e) => { + println!("Failed to decode base64: {:?}", e); + return None; + } + }; + + let decoded_token = match String::from_utf8(data) { + Ok(s) => s, + Err(e) => { + println!("Failed to convert decoded bytes to UTF-8 string: {:?}", e); + return None; + } + }; + + let (login_username, login_hash) = match decoded_token.split_once(' ') { + Some(parts) => parts, + None => { + println!("Failed to split hashed basic token into username and hash"); + return None; + } + }; + + let user_result = sqlx::query_as!( + User, + "SELECT * FROM users WHERE username = $1", + login_username + ) + .fetch_one(connection) + .await; + + let user = match user_result { + Ok(u) => u, + Err(e) => { + println!("Failed to fetch user from database: {:?}", e); + return None; + } + }; + + let password_valid = user.verify_hashed_password(login_hash); + + if password_valid == Some(true) { + Some((user, None)) + } else { + None + } +} + pub async fn read_bearer_auth_token( connection: &Pool, token: &str, @@ -307,6 +361,7 @@ impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> let data = match auth_type { "Basic" if !FORCE_BEARER_AUTH => read_basic_auth_token(pool, token).await, + "HashedBasic" if !FORCE_BEARER_AUTH => read_hashed_basic_auth_token(pool, token).await, "Bearer" => read_bearer_auth_token(pool, token).await, _ => return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)), }; diff --git a/src/nnid/agreements.rs b/src/nnid/agreements.rs index cc97c70..4fa2d59 100644 --- a/src/nnid/agreements.rs +++ b/src/nnid/agreements.rs @@ -1,37 +1,64 @@ -use std::{env, io}; -use std::collections::HashSet; +use std::{env, io, fs}; +use serde::Deserialize; use once_cell::sync::Lazy; use rocket::fs::NamedFile; -use rocket::{get, Request}; -use rocket::http::Status; -use rocket::request::{FromRequest, Outcome}; +use rocket::get; use rocket::response::content::RawXml; use tokio::fs::try_exists; -use tokio::sync::RwLock; -use rocket::async_trait; use crate::dsresponse::Ds; -pub static EVIL_AGREEMENT_THING: Lazy>> = Lazy::new(|| Default::default()); +// pub static EVIL_AGREEMENT_THING: Lazy>> = Lazy::new(|| Default::default()); -pub struct CFIP(pub String); +// pub struct CFIP(pub String); -#[async_trait] -impl<'r> FromRequest<'r> for CFIP{ - type Error = (); +// #[async_trait] +// impl<'r> FromRequest<'r> for CFIP{ +// type Error = (); - async fn from_request(request: &'r Request<'_>) -> Outcome { - match request.headers().get("CF-Connecting-IP").next(){ - Some(v) => Outcome::Success(Self(v.to_owned())), - None => Outcome::Error((Status::ImATeapot, ())) - } - } +// async fn from_request(request: &'r Request<'_>) -> Outcome { +// match request.headers().get("CF-Connecting-IP").next(){ +// Some(v) => Outcome::Success(Self(v.to_owned())), +// None => Outcome::Error((Status::ImATeapot, ())) +// } +// } +// } + +#[derive(Debug, Deserialize)] +struct Agreement { + version: String, } +#[derive(Debug, Deserialize)] +struct Agreements { + #[serde(rename = "agreement")] + agreements: Vec, +} + +pub fn get_latest_eula_version() -> Result> { + let mut path = std::env::current_dir()?; + path.push("res"); + path.push("agreement"); + path.push("DEFAULT.xml"); + + let content = fs::read_to_string(path)?; + let parsed: Agreements = quick_xml::de::from_str(&content)?; + + parsed + .agreements + .first() + .map(|a| a.version.clone()) + .ok_or_else(|| "No agreement found in XML".into()) +} + +pub static LATEST_EULA_VERSION: Lazy = Lazy::new(|| { + get_latest_eula_version() + .ok() + .and_then(|v| v.parse::().ok()) + .unwrap_or(300) +}); #[get("/v1/api/content/agreements/Nintendo-Network-EULA//@latest")] -pub async fn get_agreement(lang: &str, ip: CFIP) -> io::Result>>{ - - +pub async fn get_agreement(lang: &str) -> io::Result>>{ let base_path = { // if this crashes then something is wrong with the server setup so crashing here is fine imo let mut path = env::current_dir().unwrap(); @@ -42,38 +69,26 @@ pub async fn get_agreement(lang: &str, ip: CFIP) -> io::Result, data: Form>, - ip: CFIP, cert: DeviceCert, ) -> Result, Option>> { let pool = pool.inner(); @@ -133,14 +139,13 @@ pub async fn generate_token( return Err(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS)); } + if user.eula_version != *LATEST_EULA_VERSION { + return Err(Some(REREAD_EULA_ERRORS)); + } + if user.account_level < 0 { if user.account_level == -2 { - return Err(Some(REREAD_EULA_EXTRABANNED_ERRORS)); - } - if user.account_level == -3 { - EVIL_AGREEMENT_THING.write().await.insert(ip.0); - - return Err(Some(REREAD_EULA_EXTRABANNED_ERRORS)); + return Err(Some(ACCOUNT_TEMPBANNED_ERRORS)); } return Err(Some(ACCOUNT_BANNED_ERRORS)); } diff --git a/src/nnid/people.rs b/src/nnid/people.rs index 211293a..8154392 100644 --- a/src/nnid/people.rs +++ b/src/nnid/people.rs @@ -17,7 +17,7 @@ use rocket::{State, get, post, put}; const DATABASE_ERROR: Errors = Errors { error: &[Error { - code: "9999", + code: "2001", message: "Internal server error", }], }; @@ -494,5 +494,37 @@ pub async fn change_mii( Ok(()) } -#[post("/v1/api/people/@me/agreements")] -pub async fn thing() {} +#[derive(Deserialize, Debug)] +pub struct AgreedEulaData { + pub agreement_date: NaiveDateTime, + pub country: Box, + pub location: Box, + pub version: i32 +} + +#[post("/v1/api/people/@me/agreements", data = "")] +pub async fn thing( + database: &State, + auth: Auth, + data: Xml, +) -> Result<(), Option>> { + let db = database.inner(); + let version = data.version; + let pid = auth.pid; + println!("eula data: {:?}", data.version); + + let result = sqlx::query!( + "UPDATE users SET eula_version = $1 WHERE pid = $2", + version, + pid + ) + .execute(db) + .await; + + if let Err(e) = result { + println!("Failed to update EULA version for PID {}: {:?}", pid, e); + return Err(Some(DATABASE_ERROR)); + } + + Ok(()) +} From b0221e55d58984066656d4faee92e997dac3f319 Mon Sep 17 00:00:00 2001 From: red binder Date: Sun, 9 Aug 2026 14:55:51 +0200 Subject: [PATCH 27/32] run sqlx prepare --- ...6c32b3d302d083ec5333f3a5ce04113c38a041100.json | 12 ++++++++++++ ...6411f73f1d281a736aa5a838ad3e72fce0e9a109e.json | 15 +++++++++++++++ ...95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json | 12 ++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 .sqlx/query-7e74c28ce6daa8155e268756411f73f1d281a736aa5a838ad3e72fce0e9a109e.json diff --git a/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json b/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json index 3604678..c5e2c51 100644 --- a/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json +++ b/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json @@ -211,6 +211,17 @@ "name": "verification_code" } } + }, + { + "ordinal": 19, + "name": "eula_version", + "type_info": "Int4", + "origin": { + "Table": { + "table": "users", + "name": "eula_version" + } + } } ], "parameters": { @@ -237,6 +248,7 @@ false, false, false, + false, false ] }, diff --git a/.sqlx/query-7e74c28ce6daa8155e268756411f73f1d281a736aa5a838ad3e72fce0e9a109e.json b/.sqlx/query-7e74c28ce6daa8155e268756411f73f1d281a736aa5a838ad3e72fce0e9a109e.json new file mode 100644 index 0000000..f088075 --- /dev/null +++ b/.sqlx/query-7e74c28ce6daa8155e268756411f73f1d281a736aa5a838ad3e72fce0e9a109e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET eula_version = $1 WHERE pid = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "7e74c28ce6daa8155e268756411f73f1d281a736aa5a838ad3e72fce0e9a109e" +} diff --git a/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json b/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json index c06bdc6..d719934 100644 --- a/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json +++ b/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json @@ -211,6 +211,17 @@ "name": "verification_code" } } + }, + { + "ordinal": 19, + "name": "eula_version", + "type_info": "Int4", + "origin": { + "Table": { + "table": "users", + "name": "eula_version" + } + } } ], "parameters": { @@ -237,6 +248,7 @@ false, false, false, + false, false ] }, From acb04bd782ec1eb6b5be42406086420c7528eb43 Mon Sep 17 00:00:00 2001 From: red binder Date: Mon, 10 Aug 2026 04:39:06 +0200 Subject: [PATCH 28/32] load oauth clients from db instead of env and hardcoding --- src/json_api/oauth/generate_token.rs | 70 ++++++++++++++++++---------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/json_api/oauth/generate_token.rs b/src/json_api/oauth/generate_token.rs index 091a99f..ee5aaf9 100644 --- a/src/json_api/oauth/generate_token.rs +++ b/src/json_api/oauth/generate_token.rs @@ -3,16 +3,11 @@ use crate::nnid::oauth::generate_token::create_token; use crate::nnid::oauth::generate_token::token_type::AUTH_TOKEN; use bytemuck::bytes_of; use chrono::Utc; -use once_cell::sync::Lazy; use rocket::FromForm; use rocket::{State, form::Form, http::Status, post, serde::json::Json}; use serde::Serialize; use sha2::{Digest, Sha256}; use sqlx::Row; -use std::env; - -pub static CLIENT_SECRET: Lazy = - Lazy::new(|| env::var("OAUTH_CLIENT_SECRET").expect("OAUTH_CLIENT_SECRET not set")); #[derive(Serialize)] pub struct OAuthTokenResponse { @@ -38,10 +33,9 @@ pub fn verify_nintendo_password(pid: i32, text_password: &str, db_bcrypt_hash: & sha.update(&[0x02, 0x65, 0x43, 0x46]); sha.update(text_password.as_bytes()); let hashed_password_hex = hex::encode(sha.finalize()); - bcrypt::verify(hashed_password_hex, db_bcrypt_hash).unwrap_or_else(|_| false) + bcrypt::verify(hashed_password_hex, db_bcrypt_hash).unwrap_or(false) } -// dummy error responses #[derive(Serialize)] pub struct OAuthErrorResponse { pub error: String, @@ -54,24 +48,50 @@ pub async fn generate_token( ) -> Result, (Status, Json)> { println!("redirect URI is: {:?}", form_data.redirect_uri); - match form_data.client_id { - Some("account") | Some("splatnet") => { - let cl_secret: String = CLIENT_SECRET.clone(); - if form_data.client_secret != Some(&cl_secret) { - return Err(( - Status::Unauthorized, - Json(OAuthErrorResponse { - error: "invalid_client".to_string(), - }), - )); - } - } - Some("website") => { - // no secret for this client, cant be kept confidential in the case of the website - } + let client_id = form_data.client_id.ok_or(( + Status::BadRequest, + Json(OAuthErrorResponse { + error: "invalid_client".to_string(), + }), + ))?; + + let client_row = match sqlx::query( + "SELECT client_secret, is_confidential FROM oauth_clients WHERE client_id = $1", + ) + .bind(client_id) + .fetch_optional(pool.inner()) + .await + { + Ok(Some(row)) => row, _ => { return Err(( - Status::BadRequest, + Status::Unauthorized, + Json(OAuthErrorResponse { + error: "invalid_client".to_string(), + }), + )); + } + }; + + let db_client_secret: Option = client_row.get("client_secret"); + let is_confidential: bool = client_row.get("is_confidential"); + + if is_confidential { + let provided_secret = form_data.client_secret.ok_or(( + Status::Unauthorized, + Json(OAuthErrorResponse { + error: "invalid_client".to_string(), + }), + ))?; + + let matches = match db_client_secret { + Some(secret) => secret == provided_secret, + None => false, + }; + + if !matches { + return Err(( + Status::Unauthorized, Json(OAuthErrorResponse { error: "invalid_client".to_string(), }), @@ -79,7 +99,7 @@ pub async fn generate_token( } } - // i'm only supporting the password grant incase someone feels lazy. + // i'm only supporting the password grant for the id server. let pid: i32 = match form_data.grant_type { "password" => { let username = form_data.username.ok_or(( @@ -190,4 +210,4 @@ pub async fn generate_token( token_type: "Bearer".to_string(), expires_in: 3600, })) -} +} \ No newline at end of file From 672674f7adf01830247a737b9dff42b4a5e199cb Mon Sep 17 00:00:00 2001 From: red binder Date: Mon, 10 Aug 2026 21:39:11 +0200 Subject: [PATCH 29/32] implement missing routes --- ...e4a98b37c62d596cb0c8c3d1cc1c0199dcb2d.json | 16 + ...00b9bb8716320c6b421395a04f12bd826a469.json | 22 ++ ...192a7dfa8af98c00394978a9627b5ecdae9b5.json | 14 + res/agreement/DEFAULT.xml | 2 +- res/email/confirmationTemplate.html | 19 +- res/email/resetTemplate.html | 190 ++++++++++++ src/email.rs | 34 ++ src/main.rs | 8 + src/nnid/people.rs | 290 +++++++++++++++++- src/nnid/support.rs | 75 ++++- 10 files changed, 639 insertions(+), 31 deletions(-) create mode 100644 .sqlx/query-0edf3f60bfb92912f73b6c61962e4a98b37c62d596cb0c8c3d1cc1c0199dcb2d.json create mode 100644 .sqlx/query-8cd49b3defe144fe73bdeee1ce000b9bb8716320c6b421395a04f12bd826a469.json create mode 100644 .sqlx/query-ffef185a4db4b101477f7a78444192a7dfa8af98c00394978a9627b5ecdae9b5.json create mode 100644 res/email/resetTemplate.html diff --git a/.sqlx/query-0edf3f60bfb92912f73b6c61962e4a98b37c62d596cb0c8c3d1cc1c0199dcb2d.json b/.sqlx/query-0edf3f60bfb92912f73b6c61962e4a98b37c62d596cb0c8c3d1cc1c0199dcb2d.json new file mode 100644 index 0000000..1ae8535 --- /dev/null +++ b/.sqlx/query-0edf3f60bfb92912f73b6c61962e4a98b37c62d596cb0c8c3d1cc1c0199dcb2d.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE users SET\n email = $1,\n email_verified_since = NULL,\n verification_code = $2\n WHERE pid = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Int4", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "0edf3f60bfb92912f73b6c61962e4a98b37c62d596cb0c8c3d1cc1c0199dcb2d" +} diff --git a/.sqlx/query-8cd49b3defe144fe73bdeee1ce000b9bb8716320c6b421395a04f12bd826a469.json b/.sqlx/query-8cd49b3defe144fe73bdeee1ce000b9bb8716320c6b421395a04f12bd826a469.json new file mode 100644 index 0000000..6ec9f3a --- /dev/null +++ b/.sqlx/query-8cd49b3defe144fe73bdeee1ce000b9bb8716320c6b421395a04f12bd826a469.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE users SET\n gender = COALESCE($1, gender),\n region = COALESCE($2, region),\n country = COALESCE($3, country),\n language = COALESCE($4, language),\n timezone = COALESCE($5, timezone),\n marketing_allowed = COALESCE($6, marketing_allowed),\n off_device_allowed = COALESCE($7, off_device_allowed),\n password = COALESCE($8, password)\n WHERE pid = $9\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Int4", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Bool", + "Varchar", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "8cd49b3defe144fe73bdeee1ce000b9bb8716320c6b421395a04f12bd826a469" +} diff --git a/.sqlx/query-ffef185a4db4b101477f7a78444192a7dfa8af98c00394978a9627b5ecdae9b5.json b/.sqlx/query-ffef185a4db4b101477f7a78444192a7dfa8af98c00394978a9627b5ecdae9b5.json new file mode 100644 index 0000000..e9fb825 --- /dev/null +++ b/.sqlx/query-ffef185a4db4b101477f7a78444192a7dfa8af98c00394978a9627b5ecdae9b5.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM users WHERE pid = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [] + }, + "hash": "ffef185a4db4b101477f7a78444192a7dfa8af98c00394978a9627b5ecdae9b5" +} diff --git a/res/agreement/DEFAULT.xml b/res/agreement/DEFAULT.xml index 25d2187..e6ba92a 100644 --- a/res/agreement/DEFAULT.xml +++ b/res/agreement/DEFAULT.xml @@ -1 +1 @@ -USenEnglish2014-09-29T20:07:35NINTENDO-NETWORK-EULA0300USenEspañol2014-09-29T20:07:35NINTENDO-NETWORK-EULA0300 \ No newline at end of file +USenEnglish2014-09-29T20:07:35NINTENDO-NETWORK-EULA0300USenEspañol2014-09-29T20:07:35NINTENDO-NETWORK-EULA0300 \ No newline at end of file diff --git a/res/email/confirmationTemplate.html b/res/email/confirmationTemplate.html index 63753f8..ecb52b3 100644 --- a/res/email/confirmationTemplate.html +++ b/res/email/confirmationTemplate.html @@ -38,7 +38,7 @@ color: #B60000 !important; } img.logo { - content: url("https://spfn.net/res/img/spfn.png") !important; + content: url("https://spfn.spbr.net/res/img/spfn.png") !important; } } @media (prefers-color-scheme: dark) { @@ -91,8 +91,8 @@ @@ -122,19 +122,6 @@ Your Splatfestival Network ID activation is almost complete. - - - - - - - - - - - - -
- - + +
 
 
Enter the following 6-digit code on your console: diff --git a/res/email/resetTemplate.html b/res/email/resetTemplate.html new file mode 100644 index 0000000..84b899a --- /dev/null +++ b/res/email/resetTemplate.html @@ -0,0 +1,190 @@ + + + + + + + + +
Hello {{username}}. Your Splatfestival Network ID password reset has been processed. You may use the temporary password below to log into your account.
+ + + + +
+ + + + +
+ + + + + + +
  + + + + + + + +
 
+ + + + + + + + + + + + + + + + + + + +
+ + + +
 
+ + + + + + +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ Hello {{username}}. +
 
+ Your Splatfestival Network ID password reset has been processed. +
+ You may use the following temporary password to login: +
 
+ {{password}} +
 
+ Please change your password as soon as you can on your Wii U. (Click your mii on the top-left of the Wii U Menu and scroll to "Change Password") +
 
+ The SPFN team +
 
+
 
+
 
+ Note: this email message was auto-generated, please do not respond. For further assistance, please join our Discord server. +
 
+
+
 
+
+
+ + \ No newline at end of file diff --git a/src/email.rs b/src/email.rs index 255fe49..4008994 100644 --- a/src/email.rs +++ b/src/email.rs @@ -36,3 +36,37 @@ pub async fn send_verification_email(to: &str, code: i32, username: &str) -> Res Ok(()) } + +pub async fn send_reset_email(to: &str, pwd: &str, username: &str) -> Result<(), String> { + let smtp_user = env::var("SMTP_USER").map_err(|_| "SMTP_USER not set".to_string())?; + let smtp_pass = env::var("SMTP_PASS").map_err(|_| "SMTP_PASS not set".to_string())?; + let smtp_server = env::var("SMTP_SERVER").map_err(|_| "SMTP_SERVER not set".to_string())?; + + // Load template + let template = fs::read_to_string("res/email/resetTemplate.html") + .map_err(|e| format!("Failed to read email template: {}", e))?; + + // Replace placeholders + let body = template + .replace("{{username}}", username) + .replace("{{password}}", pwd); + + let email = Message::builder() + .from(smtp_user.parse().unwrap()) + .to(to.parse().unwrap()) + .subject("Password Reset for SPFN") + .header(lettre::message::header::ContentType::TEXT_HTML) + .body(body) + .map_err(|e| e.to_string())?; + + let creds = Credentials::new(smtp_user, smtp_pass); + + let mailer = SmtpTransport::relay(&smtp_server) + .map_err(|e| e.to_string())? + .credentials(creds) + .build(); + + mailer.send(&email).map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 1ad5ca5..cee7cc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,11 +101,19 @@ async fn launch() -> _ { nnid::support::validate, nnid::support::verify_email, nnid::support::resend_email, + nnid::support::forgotten_password, nnid::people::create_account, nnid::people::get_own_profile, nnid::people::get_device_owner, nnid::people::get_own_device, nnid::people::change_mii, + nnid::people::update_account, + nnid::people::delete_account, + nnid::people::get_user_devices, + nnid::people::get_device_status, + nnid::people::inactivate_current_device, + nnid::people::get_own_emails, + nnid::people::update_primary_email, nnid::miis::get_miis, nnid::oauth::generate_token::generate_token, nnid::provider::get_nex_token, diff --git a/src/nnid/people.rs b/src/nnid/people.rs index 8154392..5f45c08 100644 --- a/src/nnid/people.rs +++ b/src/nnid/people.rs @@ -13,6 +13,7 @@ use nex_account::grpc::{ActStageInfo, ActStageReturn}; use nex_account::grpc_client; use rand::prelude::*; use rocket::serde::{Deserialize, Serialize}; +use rocket::request::{FromRequest, Outcome, Request}; use rocket::{State, get, post, put}; const DATABASE_ERROR: Errors = Errors { @@ -64,6 +65,67 @@ pub struct AccountCreationResponseData { pid: i32, } +#[derive(Serialize)] +#[serde(rename = "device")] +pub struct DeviceInfo { + pub device_id: String, + pub language: String, + pub updated: NaiveDateTime, + pub pid: i32, + pub platform_id: String, + pub region: String, + pub serial_number: String, + pub status: String, + pub system_version: String, + pub r#type: String, + pub updated_by: String, +} + +#[derive(Serialize)] +pub struct DevicesWrapper { + #[serde(rename = "device")] + pub devices: Vec, +} + +#[derive(Serialize)] +pub struct EmptyDeviceResponse { + pub device: String, +} + +#[derive(Deserialize)] +#[serde(rename = "person")] +pub struct UpdateAccountData { + pub gender: Option>, + pub region: Option, + pub country: Option>, + pub language: Option>, + pub tz_name: Option>, + pub marketing_flag: Option, + pub off_device_flag: Option, + pub password: Option>, +} + +#[derive(Serialize)] +pub struct EmailWrapper { + pub email: EmailInfoOwnProfileData, +} + +#[derive(Serialize)] +pub struct EmailsWrapper { + #[serde(rename = "email")] + pub emails: Vec, +} + +#[derive(Deserialize)] +pub struct UpdateEmailData { + pub address: Box, +} + +#[derive(Deserialize)] +pub struct UpdateEmailRequest { + pub email: UpdateEmailData, +} + #[post("/v1/api/people", data = "")] pub async fn create_account( database: &State, @@ -174,17 +236,17 @@ pub async fn create_account( // } #[derive(Serialize)] -struct EmailInfoOwnProfileData { - address: String, - id: u32, - parent: YesNoVal, - primary: YesNoVal, - reachable: YesNoVal, +pub struct EmailInfoOwnProfileData { + pub address: String, + pub id: u32, + pub parent: YesNoVal, + pub primary: YesNoVal, + pub reachable: YesNoVal, #[serde(rename = "type")] - email_type: String, - updated_by: String, - validated: YesNoVal, - validated_date: Option, + pub email_type: String, + pub updated_by: String, + pub validated: YesNoVal, + pub validated_date: Option, } #[derive(Serialize)] @@ -525,6 +587,214 @@ pub async fn thing( println!("Failed to update EULA version for PID {}: {:?}", pid, e); return Err(Some(DATABASE_ERROR)); } + + Ok(()) +} + +pub struct DeviceHeaders { + pub device_id: String, + pub accept_language: String, + pub platform_id: String, + pub region: String, + pub serial_number: String, + pub system_version: String, +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for DeviceHeaders { + type Error = Errors<'static>; + + async fn from_request(req: &'r Request<'_>) -> Outcome { + let headers = req.headers(); + + let get_h = |key: &str| headers.get_one(key).map(|s| s.to_string()); + + match ( + get_h("x-nintendo-device-id"), + get_h("accept-language"), + get_h("x-nintendo-platform-id"), + get_h("x-nintendo-region"), + get_h("x-nintendo-serial-number"), + get_h("x-nintendo-system-version"), + ) { + ( + Some(device_id), + Some(accept_language), + Some(platform_id), + Some(region), + Some(serial_number), + Some(system_version), + ) => Outcome::Success(DeviceHeaders { + device_id, + accept_language, + platform_id, + region, + serial_number, + system_version, + }), + _ => Outcome::Error(( + rocket::http::Status::BadRequest, + Errors { + error: &[Error { + code: "1600", + message: "Unable to process request", + }], + }, + )), + } + } +} + +#[get("/v1/api/people/@me/devices")] +pub fn get_user_devices( + auth: Auth, + headers: DeviceHeaders, +) -> Xml { + let now = chrono::Utc::now().naive_utc(); + + Xml(DevicesWrapper { + devices: vec![DeviceInfo { + device_id: headers.device_id, + language: headers.accept_language, + updated: now, + pid: auth.pid, + platform_id: headers.platform_id, + region: headers.region, + serial_number: headers.serial_number, + status: "ACTIVE".to_string(), + system_version: headers.system_version, + r#type: "RETAIL".to_string(), + updated_by: "USER".to_string(), + }], + }) +} + +#[get("/v1/api/people/@me/devices/status")] +pub fn get_device_status(_auth: Auth) -> Xml { + Xml(EmptyDeviceResponse { + device: String::new(), + }) +} + +#[put("/v1/api/people/@me/devices/@current/inactivate")] +pub fn inactivate_current_device(_auth: Auth) -> () { + // just 200 +} + +#[post("/v1/api/people/@me/deletion")] +pub async fn delete_account( + database: &State, + auth: Auth, +) -> Result<(), Option>> { + let db = database.inner(); + + let result = sqlx::query!( + "DELETE FROM users WHERE pid = $1", + auth.pid + ) + .execute(db) + .await; + + if let Err(e) = result { + println!("failed to delete PID {}: {:?}", auth.pid, e); + return Err(Some(DATABASE_ERROR)); + } Ok(()) } + +#[put("/v1/api/people/@me", data = "")] +pub async fn update_account( + database: &State, + auth: Auth, + data: Xml, +) -> Result<(), Option>> { + let db = database.inner(); + let pid = auth.pid; + let data = data.0; + + let updated_password = if let Some(ref new_pass) = data.password { + generate_password(pid, new_pass) + } else { + None + }; + + let result = sqlx::query!( + " + UPDATE users SET + gender = COALESCE($1, gender), + region = COALESCE($2, region), + country = COALESCE($3, country), + language = COALESCE($4, language), + timezone = COALESCE($5, timezone), + marketing_allowed = COALESCE($6, marketing_allowed), + off_device_allowed = COALESCE($7, off_device_allowed), + password = COALESCE($8, password) + WHERE pid = $9 + ", + data.gender.as_deref(), + data.region, + data.country.as_deref(), + data.language.as_deref(), + data.tz_name.as_deref(), + data.marketing_flag.map(|v| v.0), + data.off_device_flag.map(|v| v.0), + updated_password, + pid + ) + .execute(db) + .await; + + if let Err(e) = result { + println!("failed to update account for PID {}: {:?}", pid, e); + return Err(Some(DATABASE_ERROR)); + } + + Ok(()) +} + +#[get("/v1/api/people/@me/emails")] +pub fn get_own_emails(user: Auth) -> Xml { + let profile = build_profile(user.into()); + Xml(EmailsWrapper { + emails: vec![profile.email], + }) +} + +#[put("/v1/api/people/@me/emails/@primary", data = "")] +pub async fn update_primary_email( + database: &State, + auth: Auth, + data: Xml, +) -> Result<(), Option>> { + let db = database.inner(); + let pid = auth.pid; + let new_address = data.0.email.address.to_lowercase(); + let verification_code: i32 = rand::rng().random_range(100_000..1_000_000); + + let result = sqlx::query!( + " + UPDATE users SET + email = $1, + email_verified_since = NULL, + verification_code = $2 + WHERE pid = $3 + ", + new_address, + verification_code, + pid + ) + .execute(db) + .await; + + if let Err(e) = result { + println!("failed to update email for PID {}: {:?}", pid, e); + return Err(Some(DATABASE_ERROR)); + } + + if let Err(e) = send_verification_email(&new_address, verification_code, &auth.username).await { + println!("failed to send verification email: {e}"); + } + + Ok(()) +} \ No newline at end of file diff --git a/src/nnid/support.rs b/src/nnid/support.rs index 715ca1c..aa63349 100644 --- a/src/nnid/support.rs +++ b/src/nnid/support.rs @@ -6,6 +6,8 @@ use rocket::form::Form; use rocket::{FromForm, State, post, put, get, Request}; use rocket::request::{self, FromRequest}; use rocket::http::Status; +use rand::RngExt; +use rand::distr::Alphanumeric; const BAD_CODE_ERROR: Errors = Errors { error: &[Error { @@ -14,6 +16,13 @@ const BAD_CODE_ERROR: Errors = Errors { }], }; +const UNAUTHORIZED_DEVICE_ERROR: Errors = Errors { + error: &[Error { + code: "0113", + message: "Unauthorized device", + }], +}; + #[derive(FromForm)] pub struct ValidateEmailInput { email: String, @@ -45,7 +54,6 @@ pub async fn validate( let email = data.email.trim(); - // 1. Validate presence + basic format if email.is_empty() || !email.contains('@') { return Err(Errors { error: &[Error { @@ -55,7 +63,6 @@ pub async fn validate( }); } - // 2. Extract domain safely let domain = match email.split('@').nth(1) { Some(d) if !d.is_empty() => d, _ => { @@ -68,7 +75,7 @@ pub async fn validate( } }; - // 3. DNS resolver + // This shouldn't ever fail unless there's something wrong with the server let resolver = TokioAsyncResolver::tokio_from_system_conf() .map_err(|_| Errors { error: &[Error { @@ -77,7 +84,6 @@ pub async fn validate( }], })?; - // 4. MX lookup match resolver.mx_lookup(domain).await { Ok(mx) if mx.iter().next().is_some() => Ok(()), @@ -159,5 +165,66 @@ pub async fn resend_email( BAD_CODE_ERROR })?; + Ok(()) +} + +#[get("/v1/api/support/forgotten_password/")] +pub async fn forgotten_password( + database: &State, + pid: i32, +) -> Result<(), Errors<'static>> { + let db = database.inner(); + + let user_data = sqlx::query!( + "SELECT username, email FROM users WHERE pid = $1", + pid + ) + .fetch_optional(db) + .await + .map_err(|e| { + eprintln!("database lookup error: {e}"); + UNAUTHORIZED_DEVICE_ERROR + })?; + + let user = match user_data { + Some(u) => u, + None => return Err(UNAUTHORIZED_DEVICE_ERROR), + }; + + let cleartext_password: String = rand::rng() + .sample_iter(&Alphanumeric) + .take(10) + .map(char::from) + .collect(); + + let nintendo_hash = crate::account::account::generate_password(pid, &cleartext_password) + .ok_or(UNAUTHORIZED_DEVICE_ERROR)?; + + let hashed_password = bcrypt::hash(nintendo_hash, 10) + .map_err(|e| { + eprintln!("bcrypt error: {e}"); + UNAUTHORIZED_DEVICE_ERROR + })?; + + let update_result = sqlx::query!( + "UPDATE users SET password = $1 WHERE pid = $2", + hashed_password, + pid + ) + .execute(db) + .await; + + if let Err(e) = update_result { + eprintln!("failed to update password for PID {pid}: {e}"); + return Err(UNAUTHORIZED_DEVICE_ERROR); + } + + crate::email::send_reset_email(&user.email, &cleartext_password, &user.username) + .await + .map_err(|e| { + eprintln!("failed to send reset email: {e}"); + UNAUTHORIZED_DEVICE_ERROR + })?; + Ok(()) } \ No newline at end of file From fa063d78fb25f37df4ab476b0d404f7730f90db6 Mon Sep 17 00:00:00 2001 From: red binder Date: Mon, 10 Aug 2026 22:08:17 +0200 Subject: [PATCH 30/32] fix bug and sqlx prepare --- ...3de747bba2ce1da198dda4b6f3b78eacfebf3.json | 40 +++++++++++++++++++ ...2256be431b361cfd36754aa41b6da220618cc.json | 15 +++++++ src/nnid/support.rs | 8 +--- 3 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 .sqlx/query-48fd98db66a68865babe4ac6a453de747bba2ce1da198dda4b6f3b78eacfebf3.json create mode 100644 .sqlx/query-6c276038b92bef06b30da32dab42256be431b361cfd36754aa41b6da220618cc.json diff --git a/.sqlx/query-48fd98db66a68865babe4ac6a453de747bba2ce1da198dda4b6f3b78eacfebf3.json b/.sqlx/query-48fd98db66a68865babe4ac6a453de747bba2ce1da198dda4b6f3b78eacfebf3.json new file mode 100644 index 0000000..2619390 --- /dev/null +++ b/.sqlx/query-48fd98db66a68865babe4ac6a453de747bba2ce1da198dda4b6f3b78eacfebf3.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT username, email FROM users WHERE pid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "username", + "type_info": "Varchar", + "origin": { + "Table": { + "table": "users", + "name": "username" + } + } + }, + { + "ordinal": 1, + "name": "email", + "type_info": "Varchar", + "origin": { + "Table": { + "table": "users", + "name": "email" + } + } + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "48fd98db66a68865babe4ac6a453de747bba2ce1da198dda4b6f3b78eacfebf3" +} diff --git a/.sqlx/query-6c276038b92bef06b30da32dab42256be431b361cfd36754aa41b6da220618cc.json b/.sqlx/query-6c276038b92bef06b30da32dab42256be431b361cfd36754aa41b6da220618cc.json new file mode 100644 index 0000000..1e5c7eb --- /dev/null +++ b/.sqlx/query-6c276038b92bef06b30da32dab42256be431b361cfd36754aa41b6da220618cc.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET password = $1 WHERE pid = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "6c276038b92bef06b30da32dab42256be431b361cfd36754aa41b6da220618cc" +} diff --git a/src/nnid/support.rs b/src/nnid/support.rs index aa63349..aac67fc 100644 --- a/src/nnid/support.rs +++ b/src/nnid/support.rs @@ -200,15 +200,9 @@ pub async fn forgotten_password( let nintendo_hash = crate::account::account::generate_password(pid, &cleartext_password) .ok_or(UNAUTHORIZED_DEVICE_ERROR)?; - let hashed_password = bcrypt::hash(nintendo_hash, 10) - .map_err(|e| { - eprintln!("bcrypt error: {e}"); - UNAUTHORIZED_DEVICE_ERROR - })?; - let update_result = sqlx::query!( "UPDATE users SET password = $1 WHERE pid = $2", - hashed_password, + nintendo_hash, pid ) .execute(db) From 124916e62cff7e0a79ea7a022b49c2a1aaf962ee Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 11 Aug 2026 03:34:00 +0200 Subject: [PATCH 31/32] parental controls --- ...4f25f0636ec0e7c0beed4cdb627c419e407c4.json | 15 ++ res/email/masterKeyTemplate.html | 190 ++++++++++++++++++ res/email/parentalControlsTemplate.html | 177 ++++++++++++++++ src/email.rs | 67 ++++++ src/main.rs | 2 + src/nnid/support.rs | 105 ++++++++++ 6 files changed, 556 insertions(+) create mode 100644 .sqlx/query-7d5d57a9c0065301382767c85de4f25f0636ec0e7c0beed4cdb627c419e407c4.json create mode 100644 res/email/masterKeyTemplate.html create mode 100644 res/email/parentalControlsTemplate.html diff --git a/.sqlx/query-7d5d57a9c0065301382767c85de4f25f0636ec0e7c0beed4cdb627c419e407c4.json b/.sqlx/query-7d5d57a9c0065301382767c85de4f25f0636ec0e7c0beed4cdb627c419e407c4.json new file mode 100644 index 0000000..758b35a --- /dev/null +++ b/.sqlx/query-7d5d57a9c0065301382767c85de4f25f0636ec0e7c0beed4cdb627c419e407c4.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO device_parental_controls (serial_number, email, updated_at)\n VALUES ($1, $2, NOW())\n ON CONFLICT (serial_number)\n DO UPDATE SET email = EXCLUDED.email, updated_at = NOW()\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "7d5d57a9c0065301382767c85de4f25f0636ec0e7c0beed4cdb627c419e407c4" +} diff --git a/res/email/masterKeyTemplate.html b/res/email/masterKeyTemplate.html new file mode 100644 index 0000000..50f3f9c --- /dev/null +++ b/res/email/masterKeyTemplate.html @@ -0,0 +1,190 @@ + + + + + + + + +
Hello. You have requested a reset of parental controls on your console.
+ + + + +
+ + + + +
+ + + + + + +
  + + + + + + + +
 
+ + + + + + + + + + + + + + + + + + + +
+ + + +
 
+ + + + + + +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ Hello. +
 
+ Your email was linked to a Wii U console with serial number {{serial}} for parental controls. +
 
+ You have requested a master key to reset your parental controls PIN. The master key you must enter on your Wii U is: +
 
+ {{mkey}} +
 
+ Please make sure to change your PIN as soon as possible. +
+ The SPFN team +
 
+
 
+
 
+ Note: this email message was auto-generated, please do not respond. For further assistance, please join our Discord server. +
 
+
+
 
+
+
+ + \ No newline at end of file diff --git a/res/email/parentalControlsTemplate.html b/res/email/parentalControlsTemplate.html new file mode 100644 index 0000000..92c1cdb --- /dev/null +++ b/res/email/parentalControlsTemplate.html @@ -0,0 +1,177 @@ + + + + + + + + +
Hello. The console with the serial number {{serial}} has been linked to your email for parental controls.
+ + + + +
+ + + + +
+ + + + + + +
  + + + + + + + +
 
+ + + + + + + + + + + + + + + + + + + +
+ + + +
 
+ + + + + + +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ Hello. +
 
+ Your email was linked to a Wii U console with serial number {{serial}} for parental controls. +
 
+ If you did not authorize this, please contact our team at support@spbr.net. +
 
+ The SPFN team +
 
+
 
+
 
+ Note: this email message was auto-generated, please do not respond. For further assistance, please join our Discord server. +
 
+
+
 
+
+
+ + \ No newline at end of file diff --git a/src/email.rs b/src/email.rs index 4008994..b0ebd9f 100644 --- a/src/email.rs +++ b/src/email.rs @@ -70,3 +70,70 @@ pub async fn send_reset_email(to: &str, pwd: &str, username: &str) -> Result<(), Ok(()) } + +pub async fn send_pc_email(to: &str, serial: &str) -> Result<(), String> { + let smtp_user = env::var("SMTP_USER").map_err(|_| "SMTP_USER not set".to_string())?; + let smtp_pass = env::var("SMTP_PASS").map_err(|_| "SMTP_PASS not set".to_string())?; + let smtp_server = env::var("SMTP_SERVER").map_err(|_| "SMTP_SERVER not set".to_string())?; + + // Load template + let template = fs::read_to_string("res/email/parentalControlsTemplate.html") + .map_err(|e| format!("Failed to read email template: {}", e))?; + + // Replace placeholders + let body = template + .replace("{{serial}}", serial); + + let email = Message::builder() + .from(smtp_user.parse().unwrap()) + .to(to.parse().unwrap()) + .subject("Parental controls confirmation for SPFN") + .header(lettre::message::header::ContentType::TEXT_HTML) + .body(body) + .map_err(|e| e.to_string())?; + + let creds = Credentials::new(smtp_user, smtp_pass); + + let mailer = SmtpTransport::relay(&smtp_server) + .map_err(|e| e.to_string())? + .credentials(creds) + .build(); + + mailer.send(&email).map_err(|e| e.to_string())?; + + Ok(()) +} + +pub async fn send_reset_pin_email(to: &str, serial: &str, master_key: &str) -> Result<(), String> { + let smtp_user = env::var("SMTP_USER").map_err(|_| "SMTP_USER not set".to_string())?; + let smtp_pass = env::var("SMTP_PASS").map_err(|_| "SMTP_PASS not set".to_string())?; + let smtp_server = env::var("SMTP_SERVER").map_err(|_| "SMTP_SERVER not set".to_string())?; + + // Load template + let template = fs::read_to_string("res/email/masterKeyTemplate.html") + .map_err(|e| format!("Failed to read email template: {}", e))?; + + // Replace placeholders + let body = template + .replace("{{serial}}", serial) + .replace("{{mkey}}", master_key); + + let email = Message::builder() + .from(smtp_user.parse().unwrap()) + .to(to.parse().unwrap()) + .subject("Parental controls PIN reset for SPFN") + .header(lettre::message::header::ContentType::TEXT_HTML) + .body(body) + .map_err(|e| e.to_string())?; + + let creds = Credentials::new(smtp_user, smtp_pass); + + let mailer = SmtpTransport::relay(&smtp_server) + .map_err(|e| e.to_string())? + .credentials(creds) + .build(); + + mailer.send(&email).map_err(|e| e.to_string())?; + + Ok(()) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index cee7cc5..622d3ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -102,6 +102,8 @@ async fn launch() -> _ { nnid::support::verify_email, nnid::support::resend_email, nnid::support::forgotten_password, + nnid::support::send_parental_controls_pin, + nnid::support::send_forgotten_pin, nnid::people::create_account, nnid::people::get_own_profile, nnid::people::get_device_owner, diff --git a/src/nnid/support.rs b/src/nnid/support.rs index aac67fc..f119ef9 100644 --- a/src/nnid/support.rs +++ b/src/nnid/support.rs @@ -220,5 +220,110 @@ pub async fn forgotten_password( UNAUTHORIZED_DEVICE_ERROR })?; + Ok(()) +} + +pub struct DeviceSerial(pub String); + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for DeviceSerial { + type Error = Errors<'static>; + + async fn from_request(req: &'r Request<'_>) -> request::Outcome { + match req.headers().get_one("x-nintendo-serial-number") { + Some(val) if !val.trim().is_empty() => { + request::Outcome::Success(DeviceSerial(val.to_string())) + } + _ => request::Outcome::Error(( + Status::BadRequest, + Errors { + error: &[Error { + code: "0130", + message: "Serial not found", + }], + }, + )), + } + } +} + +#[get("/v1/api/support/send_confirmation/pin/")] +pub async fn send_parental_controls_pin( + database: &State, + serial: DeviceSerial, + email: &str, +) -> Result<(), Errors<'static>> { + let db = database.inner(); + let serial_number = serial.0; + let email = email.trim().to_lowercase(); + + if email.is_empty() || !email.contains('@') { + return Err(Errors { + error: &[Error { + code: "0103", + message: "Email format is invalid", + }], + }); + } + + let db_result = sqlx::query!( + " + INSERT INTO device_parental_controls (serial_number, email, updated_at) + VALUES ($1, $2, NOW()) + ON CONFLICT (serial_number) + DO UPDATE SET email = EXCLUDED.email, updated_at = NOW() + ", + serial_number, + email + ) + .execute(db) + .await; + + if let Err(e) = db_result { + eprintln!("Failed to associate serial {serial_number} with email {email}: {e}"); + return Err(Errors { + error: &[Error { + code: "0130", + message: "PID has not been registered yet", + }], + }); + } + + crate::email::send_pc_email(&email, &serial_number) + .await + .map_err(|e| { + eprintln!("Failed to send parental controls email to {email}: {e}"); + Errors { + error: &[Error { + code: "0130", + message: "PID has not been registered yet", + }], + } + })?; + + Ok(()) +} + +#[get("/v1/api/support/send_forgotten/pin//")] +pub async fn send_forgotten_pin( + serial: DeviceSerial, + email: &str, + master_key: &str, +) -> Result<(), Errors<'static>> { + let serial_number = serial.0; + let email = email.trim().to_lowercase(); + + crate::email::send_reset_pin_email(&email, &serial_number, &master_key) + .await + .map_err(|e| { + eprintln!("Failed to send parental controls email to {email}: {e}"); + Errors { + error: &[Error { + code: "0130", + message: "PID has not been registered yet", + }], + } + })?; + Ok(()) } \ No newline at end of file From 82b5bb36c5fd6a05b2b2539027b2bac8472fd375 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 11 Aug 2026 04:35:12 +0200 Subject: [PATCH 32/32] force bearer auth no idea if this is right but i want to protect accounts --- src/nnid/people.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nnid/people.rs b/src/nnid/people.rs index 5f45c08..c81b4b2 100644 --- a/src/nnid/people.rs +++ b/src/nnid/people.rs @@ -647,7 +647,7 @@ impl<'r> FromRequest<'r> for DeviceHeaders { #[get("/v1/api/people/@me/devices")] pub fn get_user_devices( - auth: Auth, + auth: Auth, headers: DeviceHeaders, ) -> Xml { let now = chrono::Utc::now().naive_utc(); @@ -670,21 +670,21 @@ pub fn get_user_devices( } #[get("/v1/api/people/@me/devices/status")] -pub fn get_device_status(_auth: Auth) -> Xml { +pub fn get_device_status(_auth: Auth) -> Xml { Xml(EmptyDeviceResponse { device: String::new(), }) } #[put("/v1/api/people/@me/devices/@current/inactivate")] -pub fn inactivate_current_device(_auth: Auth) -> () { +pub fn inactivate_current_device(_auth: Auth) -> () { // just 200 } #[post("/v1/api/people/@me/deletion")] pub async fn delete_account( database: &State, - auth: Auth, + auth: Auth, ) -> Result<(), Option>> { let db = database.inner(); @@ -706,7 +706,7 @@ pub async fn delete_account( #[put("/v1/api/people/@me", data = "")] pub async fn update_account( database: &State, - auth: Auth, + auth: Auth, data: Xml, ) -> Result<(), Option>> { let db = database.inner(); @@ -764,7 +764,7 @@ pub fn get_own_emails(user: Auth) -> Xml { #[put("/v1/api/people/@me/emails/@primary", data = "")] pub async fn update_primary_email( database: &State, - auth: Auth, + auth: Auth, data: Xml, ) -> Result<(), Option>> { let db = database.inner();