Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1576062160 | |||
| 2c7767124e | |||
| 3c3b8bf91c | |||
| 18da6e247b | |||
| f7d6b3ebf0 | |||
| ea5f9d1c13 |
13 changed files with 1425 additions and 1137 deletions
|
|
@ -1,2 +1,5 @@
|
|||
[target.'cfg(target_arch = "x86_64")']
|
||||
rustflags = ["-C", "target-feature=+aes,+sse2"]
|
||||
rustflags = ["-C", "target-feature=+aes,+sse2"]
|
||||
|
||||
[registries]
|
||||
spbr = { index = "sparse+https://crates.spbr.net/api/v1/crates/" }
|
||||
|
|
|
|||
15
.sqlx/query-9d41e04076235e9a8c857c58c6f5fe7b26cba4da50ce8527ba32128152a31ce6.json
generated
Normal file
15
.sqlx/query-9d41e04076235e9a8c857c58c6f5fe7b26cba4da50ce8527ba32128152a31ce6.json
generated
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE users SET account_level = $1 WHERE username = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9d41e04076235e9a8c857c58c6f5fe7b26cba4da50ce8527ba32128152a31ce6"
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT game_server_id, maintenance_mode, address, port FROM nex_servers WHERE title_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "game_server_id",
|
||||
"type_info": "Varchar",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "nex_servers",
|
||||
"name": "game_server_id"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "maintenance_mode",
|
||||
"type_info": "Bool",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "nex_servers",
|
||||
"name": "maintenance_mode"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "address",
|
||||
"type_info": "Inet",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "nex_servers",
|
||||
"name": "address"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "port",
|
||||
"type_info": "Int4",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "nex_servers",
|
||||
"name": "port"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e3349c0e5ab82bbef359cf573caf454f23588f3cfa27299e58863dc9397d55de"
|
||||
}
|
||||
1992
Cargo.lock
generated
1992
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
|
|
@ -55,3 +55,4 @@ dsa = "0.6.3"
|
|||
openssl = {version = "0.10.78", features = ["vendored"]}
|
||||
time = "0.3.47"
|
||||
hickory-resolver = { version = "0.24", features = ["tokio-runtime"] }
|
||||
nex-account = { version = "0.2.3", registry = "spbr" }
|
||||
|
|
|
|||
39
src/json_api/admin/bans.rs
Normal file
39
src/json_api/admin/bans.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
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)]
|
||||
pub struct AdminRequest<'r> {
|
||||
pub username: &'r str,
|
||||
}
|
||||
|
||||
#[post("/api/v2/admin/ban", data = "<request>")]
|
||||
pub async fn ban_user(pool: &State<Pool>, auth: Auth<true>, request: Form<AdminRequest<'_>>) -> Result<(), Status> {
|
||||
if auth.account_level < 2 {
|
||||
return Err(Status::Forbidden);
|
||||
};
|
||||
|
||||
log::info!("banning user {:?} from moderator {:?}", request.username, auth.username);
|
||||
|
||||
let row = sqlx::query!(
|
||||
"UPDATE users SET account_level = $1 WHERE username = $2",
|
||||
-1,
|
||||
request.username
|
||||
)
|
||||
.execute(pool.inner())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("failed to execute query: {:?}", e);
|
||||
return Err::<(), rocket::http::Status>(Status::InternalServerError);
|
||||
});
|
||||
|
||||
log::info!("banned user {:?}", request.username);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
1
src/json_api/admin/mod.rs
Normal file
1
src/json_api/admin/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod bans;
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod oauth;
|
||||
pub mod users;
|
||||
pub mod users;
|
||||
pub mod admin;
|
||||
|
|
@ -41,10 +41,7 @@ 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());
|
||||
match bcrypt::verify(hashed_password_hex, db_bcrypt_hash) {
|
||||
Ok(valid) => valid,
|
||||
Err(_) => false,
|
||||
}
|
||||
bcrypt::verify(hashed_password_hex, db_bcrypt_hash).unwrap_or_else(|_| false)
|
||||
}
|
||||
|
||||
// dummy error responses
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ async fn launch() -> _ {
|
|||
json_api::users::delete::delete_account,
|
||||
json_api::oauth::authorize::authorize_page,
|
||||
json_api::oauth::authorize::authorize_submit,
|
||||
json_api::admin::bans::ban_user,
|
||||
nnid::people::thing,
|
||||
// graphql::graphiql,
|
||||
// graphql::playground,
|
||||
|
|
|
|||
|
|
@ -1,31 +1,30 @@
|
|||
#![allow(unused)]
|
||||
use chrono::{NaiveDate, NaiveDateTime};
|
||||
use gxhash::{gxhash32, gxhash64};
|
||||
use rocket::{get, post, put, State};
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use crate::Pool;
|
||||
use crate::account::account::{Auth, User, generate_nex_password, generate_password};
|
||||
use crate::dsresponse::Ds;
|
||||
use crate::error::{Error, Errors};
|
||||
use crate::nnid::pid_distribution::next_pid;
|
||||
use crate::nnid::timezones::{OFFSET_FROM_TIMEZONE};
|
||||
use crate::Pool;
|
||||
use crate::xml::{Xml, YesNoVal};
|
||||
use crate::email::send_verification_email;
|
||||
use rand::prelude::*;
|
||||
use crate::error::{Error, Errors};
|
||||
use crate::mii_util::get_mii_img_url;
|
||||
use crate::nnid::timezones::OFFSET_FROM_TIMEZONE;
|
||||
use crate::xml::{Xml, YesNoVal};
|
||||
use chrono::{NaiveDate, NaiveDateTime};
|
||||
use gxhash::{gxhash32, gxhash64};
|
||||
use nex_account::grpc::{ActStageInfo, ActStageReturn};
|
||||
use nex_account::grpc_client;
|
||||
use rand::prelude::*;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use rocket::{State, get, post, put};
|
||||
|
||||
const DATABASE_ERROR: Errors = Errors{
|
||||
error: &[
|
||||
Error{
|
||||
code: "9999",
|
||||
message: "Internal server error"
|
||||
}
|
||||
]
|
||||
const DATABASE_ERROR: Errors = Errors {
|
||||
error: &[Error {
|
||||
code: "9999",
|
||||
message: "Internal server error",
|
||||
}],
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Email{
|
||||
address: Box<str>
|
||||
pub struct Email {
|
||||
address: Box<str>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -36,7 +35,7 @@ pub struct UpdateMiiData {
|
|||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Mii{
|
||||
pub struct Mii {
|
||||
name: Box<str>,
|
||||
primary: YesNoVal,
|
||||
data: Box<str>,
|
||||
|
|
@ -44,7 +43,7 @@ pub struct Mii{
|
|||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename(serialize = "person"))]
|
||||
pub struct AccountCreationData{
|
||||
pub struct AccountCreationData {
|
||||
birth_date: NaiveDate,
|
||||
user_id: Box<str>,
|
||||
password: Box<str>,
|
||||
|
|
@ -56,24 +55,38 @@ pub struct AccountCreationData{
|
|||
gender: Box<str>,
|
||||
marketing_flag: YesNoVal,
|
||||
off_device_flag: YesNoVal,
|
||||
region: i32
|
||||
region: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename(serialize = "person"))]
|
||||
pub struct AccountCreationResponseData{
|
||||
pid: i32
|
||||
pub struct AccountCreationResponseData {
|
||||
pid: i32,
|
||||
}
|
||||
|
||||
#[post("/v1/api/people", data="<data>")]
|
||||
pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationData>) -> Result<Xml<AccountCreationResponseData>, Option<Errors<'_>>>{
|
||||
#[post("/v1/api/people", data = "<data>")]
|
||||
pub async fn create_account(
|
||||
database: &State<Pool>,
|
||||
data: Xml<AccountCreationData>,
|
||||
) -> Result<Xml<AccountCreationResponseData>, Option<Errors<'_>>> {
|
||||
let database = database.inner();
|
||||
let nex_password = generate_nex_password();
|
||||
|
||||
let mut client = grpc_client().await.expect("unable to connect to grpc");
|
||||
|
||||
let Ok(ret) = client
|
||||
.stage_new_account(ActStageInfo {
|
||||
password: nex_password.clone().into_bytes(),
|
||||
})
|
||||
.await
|
||||
else {
|
||||
return Err(Some(DATABASE_ERROR));
|
||||
};
|
||||
let ActStageReturn { pid, .. } = ret.into_inner();
|
||||
|
||||
// its fine to crash here if we cant get the next pid as that is in my opinion a dead state
|
||||
// anyways as noone can register anymore, EVER
|
||||
|
||||
let pid = next_pid(database).await;
|
||||
|
||||
let verification_code: i32 = rand::rng().random_range(100_000..1_000_000);
|
||||
|
||||
let AccountCreationData {
|
||||
|
|
@ -82,14 +95,8 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
|
|||
birth_date,
|
||||
tz_name,
|
||||
language,
|
||||
email: Email{
|
||||
address
|
||||
},
|
||||
mii: Mii{
|
||||
name,
|
||||
data,
|
||||
..
|
||||
},
|
||||
email: Email { address },
|
||||
mii: Mii { name, data, .. },
|
||||
marketing_flag,
|
||||
gender,
|
||||
region,
|
||||
|
|
@ -98,17 +105,16 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
|
|||
..
|
||||
} = data.0;
|
||||
|
||||
let account_level = if user_id.to_lowercase().contains("omey"){
|
||||
let account_level = if user_id.to_lowercase().contains("omey") {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
let password = generate_password(pid, &password).ok_or(None)?;
|
||||
let nex_password = generate_nex_password();
|
||||
|
||||
sqlx::query!("
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO users (
|
||||
pid,
|
||||
username,
|
||||
|
|
@ -146,19 +152,20 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
|
|||
verification_code,
|
||||
account_level,
|
||||
nex_password
|
||||
).execute(database).await.unwrap();
|
||||
)
|
||||
.execute(database)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//generate_s3_images(pid, &data).await;
|
||||
|
||||
if let Err(e) = send_verification_email(address.as_ref(), verification_code, user_id.as_ref()).await {
|
||||
if let Err(e) =
|
||||
send_verification_email(address.as_ref(), verification_code, user_id.as_ref()).await
|
||||
{
|
||||
println!("Failed to send verification email: {e}");
|
||||
}
|
||||
|
||||
Ok(
|
||||
Xml(AccountCreationResponseData{
|
||||
pid
|
||||
})
|
||||
)
|
||||
Ok(Xml(AccountCreationResponseData { pid }))
|
||||
}
|
||||
|
||||
// #[derive(Serialize)]
|
||||
|
|
@ -167,7 +174,7 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
|
|||
// }
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct EmailInfoOwnProfileData{
|
||||
struct EmailInfoOwnProfileData {
|
||||
address: String,
|
||||
id: u32,
|
||||
parent: YesNoVal,
|
||||
|
|
@ -177,11 +184,11 @@ struct EmailInfoOwnProfileData{
|
|||
email_type: String,
|
||||
updated_by: String,
|
||||
validated: YesNoVal,
|
||||
validated_date: Option<NaiveDateTime>
|
||||
validated_date: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct EmailInfoOwnOAuthProfileData{
|
||||
struct EmailInfoOwnOAuthProfileData {
|
||||
address: String,
|
||||
id: u32,
|
||||
parent: bool,
|
||||
|
|
@ -191,40 +198,37 @@ struct EmailInfoOwnOAuthProfileData{
|
|||
email_type: String,
|
||||
updated_by: String,
|
||||
validated: bool,
|
||||
validated_date: Option<NaiveDateTime>
|
||||
validated_date: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MiiImage{
|
||||
struct MiiImage {
|
||||
cached_url: String,
|
||||
id: u32,
|
||||
url: String,
|
||||
#[serde(rename = "type")]
|
||||
image_type: String
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MiiImages{
|
||||
mii_image: MiiImage
|
||||
image_type: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MiiDataOwnProfileData{
|
||||
struct MiiImages {
|
||||
mii_image: MiiImage,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MiiDataOwnProfileData {
|
||||
status: String,
|
||||
data: String,
|
||||
id: u32,
|
||||
mii_hash: String,
|
||||
mii_images: MiiImages,
|
||||
name: String,
|
||||
primary: YesNoVal
|
||||
primary: YesNoVal,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename(serialize = "person"))]
|
||||
pub struct GetOwnProfileData{
|
||||
pub struct GetOwnProfileData {
|
||||
active_flag: YesNoVal,
|
||||
birth_date: NaiveDate,
|
||||
country: String,
|
||||
|
|
@ -246,7 +250,7 @@ pub struct GetOwnProfileData{
|
|||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename(serialize = "person"))]
|
||||
pub struct GetOwnOAuthProfileData{
|
||||
pub struct GetOwnOAuthProfileData {
|
||||
active_flag: bool,
|
||||
birth_date: NaiveDate,
|
||||
country: String,
|
||||
|
|
@ -270,17 +274,17 @@ pub struct GetOwnOAuthProfileData{
|
|||
}
|
||||
|
||||
#[get("/v1/api/people/@me/profile")]
|
||||
pub fn get_own_profile(user: Auth<false>) -> Ds<Xml<GetOwnProfileData>>{
|
||||
pub fn get_own_profile(user: Auth<false>) -> Ds<Xml<GetOwnProfileData>> {
|
||||
Ds(Xml(build_profile(user.into())))
|
||||
}
|
||||
|
||||
#[get("/v1/api/people/@me/devices/owner")]
|
||||
pub fn get_device_owner(user: Auth<false>) -> Ds<Xml<GetOwnProfileData>>{
|
||||
pub fn get_device_owner(user: Auth<false>) -> Ds<Xml<GetOwnProfileData>> {
|
||||
Ds(Xml(build_profile(user.into())))
|
||||
}
|
||||
|
||||
#[post("/v1/api/people/@me/devices")]
|
||||
pub fn get_own_device(user: Auth<false>) -> Ds<Xml<GetOwnProfileData>>{
|
||||
pub fn get_own_device(user: Auth<false>) -> Ds<Xml<GetOwnProfileData>> {
|
||||
Ds(Xml(build_profile(user.into())))
|
||||
}
|
||||
|
||||
|
|
@ -314,59 +318,58 @@ pub fn build_profile(user: User) -> GetOwnProfileData {
|
|||
.replace("\r", "")
|
||||
.replace(" ", "");
|
||||
|
||||
|
||||
GetOwnProfileData {
|
||||
active_flag: YesNoVal(true),
|
||||
pid,
|
||||
user_id: username,
|
||||
gender,
|
||||
birth_date: birthdate,
|
||||
country,
|
||||
create_date: creation_date,
|
||||
tz_name: timezone,
|
||||
language,
|
||||
updated,
|
||||
marketing_flag: YesNoVal(marketing_allowed),
|
||||
email: EmailInfoOwnProfileData {
|
||||
id: gxhash32(email.as_bytes(), 0),
|
||||
address: email,
|
||||
validated: YesNoVal(email_verified_since.is_some()),
|
||||
validated_date: email_verified_since,
|
||||
email_type: "DEFAULT".to_string(),
|
||||
updated_by: "USER".to_string(),
|
||||
reachable: YesNoVal(true),
|
||||
primary: YesNoVal(true),
|
||||
parent: YesNoVal(false),
|
||||
},
|
||||
mii: MiiDataOwnProfileData {
|
||||
id: gxhash32(mii_data.as_bytes(), 0),
|
||||
mii_hash: hex::encode(bytemuck::bytes_of(
|
||||
&(gxhash64(mii_data.as_bytes(), 1) & !(0x1000000000000000))
|
||||
)),
|
||||
name: crate::mii_util::MiiData::read(&mii_data)
|
||||
GetOwnProfileData {
|
||||
active_flag: YesNoVal(true),
|
||||
pid,
|
||||
user_id: username,
|
||||
gender,
|
||||
birth_date: birthdate,
|
||||
country,
|
||||
create_date: creation_date,
|
||||
tz_name: timezone,
|
||||
language,
|
||||
updated,
|
||||
marketing_flag: YesNoVal(marketing_allowed),
|
||||
email: EmailInfoOwnProfileData {
|
||||
id: gxhash32(email.as_bytes(), 0),
|
||||
address: email,
|
||||
validated: YesNoVal(email_verified_since.is_some()),
|
||||
validated_date: email_verified_since,
|
||||
email_type: "DEFAULT".to_string(),
|
||||
updated_by: "USER".to_string(),
|
||||
reachable: YesNoVal(true),
|
||||
primary: YesNoVal(true),
|
||||
parent: YesNoVal(false),
|
||||
},
|
||||
mii: MiiDataOwnProfileData {
|
||||
id: gxhash32(mii_data.as_bytes(), 0),
|
||||
mii_hash: hex::encode(bytemuck::bytes_of(
|
||||
&(gxhash64(mii_data.as_bytes(), 1) & !(0x1000000000000000)),
|
||||
)),
|
||||
name: crate::mii_util::MiiData::read(&mii_data)
|
||||
.map(|v| v.name)
|
||||
.unwrap_or_else(|| "INVALID".to_string()),
|
||||
primary: YesNoVal(true),
|
||||
data: mii_data,
|
||||
status: "COMPLETED".to_string(),
|
||||
mii_images: MiiImages {
|
||||
mii_image: {
|
||||
let image_url = get_mii_img_url(pid, "tga");
|
||||
let url_hash = gxhash32(image_url.as_bytes(), 0);
|
||||
MiiImage {
|
||||
image_type: "standard".to_string(),
|
||||
id: url_hash,
|
||||
url: image_url.clone(),
|
||||
cached_url: image_url,
|
||||
}
|
||||
primary: YesNoVal(true),
|
||||
data: mii_data,
|
||||
status: "COMPLETED".to_string(),
|
||||
mii_images: MiiImages {
|
||||
mii_image: {
|
||||
let image_url = get_mii_img_url(pid, "tga");
|
||||
let url_hash = gxhash32(image_url.as_bytes(), 0);
|
||||
MiiImage {
|
||||
image_type: "standard".to_string(),
|
||||
id: url_hash,
|
||||
url: image_url.clone(),
|
||||
cached_url: image_url,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
off_device_flag: YesNoVal(off_device_allowed),
|
||||
region,
|
||||
utc_offset: timezone_offset,
|
||||
account_level,
|
||||
}
|
||||
},
|
||||
off_device_flag: YesNoVal(off_device_allowed),
|
||||
region,
|
||||
utc_offset: timezone_offset,
|
||||
account_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_oauth_profile(user: User) -> GetOwnOAuthProfileData {
|
||||
|
|
@ -404,62 +407,61 @@ pub fn build_oauth_profile(user: User) -> GetOwnOAuthProfileData {
|
|||
.replace("\r", "")
|
||||
.replace(" ", "");
|
||||
|
||||
|
||||
GetOwnOAuthProfileData {
|
||||
id,
|
||||
sub,
|
||||
uid,
|
||||
active_flag: true,
|
||||
pid,
|
||||
user_id: username,
|
||||
gender,
|
||||
birth_date: birthdate,
|
||||
country,
|
||||
create_date: creation_date,
|
||||
tz_name: timezone,
|
||||
language,
|
||||
updated,
|
||||
marketing_flag: marketing_allowed,
|
||||
email: EmailInfoOwnOAuthProfileData {
|
||||
id: gxhash32(email.as_bytes(), 0),
|
||||
address: email,
|
||||
validated: email_verified_since.is_some(),
|
||||
validated_date: email_verified_since,
|
||||
email_type: "DEFAULT".to_string(),
|
||||
updated_by: "USER".to_string(),
|
||||
reachable: true,
|
||||
primary: true,
|
||||
parent: false,
|
||||
},
|
||||
mii: MiiDataOwnProfileData {
|
||||
id: gxhash32(mii_data.as_bytes(), 0),
|
||||
mii_hash: hex::encode(bytemuck::bytes_of(
|
||||
&(gxhash64(mii_data.as_bytes(), 1) & !(0x1000000000000000))
|
||||
)),
|
||||
name: crate::mii_util::MiiData::read(&mii_data)
|
||||
GetOwnOAuthProfileData {
|
||||
id,
|
||||
sub,
|
||||
uid,
|
||||
active_flag: true,
|
||||
pid,
|
||||
user_id: username,
|
||||
gender,
|
||||
birth_date: birthdate,
|
||||
country,
|
||||
create_date: creation_date,
|
||||
tz_name: timezone,
|
||||
language,
|
||||
updated,
|
||||
marketing_flag: marketing_allowed,
|
||||
email: EmailInfoOwnOAuthProfileData {
|
||||
id: gxhash32(email.as_bytes(), 0),
|
||||
address: email,
|
||||
validated: email_verified_since.is_some(),
|
||||
validated_date: email_verified_since,
|
||||
email_type: "DEFAULT".to_string(),
|
||||
updated_by: "USER".to_string(),
|
||||
reachable: true,
|
||||
primary: true,
|
||||
parent: false,
|
||||
},
|
||||
mii: MiiDataOwnProfileData {
|
||||
id: gxhash32(mii_data.as_bytes(), 0),
|
||||
mii_hash: hex::encode(bytemuck::bytes_of(
|
||||
&(gxhash64(mii_data.as_bytes(), 1) & !(0x1000000000000000)),
|
||||
)),
|
||||
name: crate::mii_util::MiiData::read(&mii_data)
|
||||
.map(|v| v.name)
|
||||
.unwrap_or_else(|| "INVALID".to_string()),
|
||||
primary: YesNoVal(true),
|
||||
data: mii_data,
|
||||
status: "COMPLETED".to_string(),
|
||||
mii_images: MiiImages {
|
||||
mii_image: {
|
||||
let image_url = get_mii_img_url(pid, "png");
|
||||
let url_hash = gxhash32(image_url.as_bytes(), 0);
|
||||
MiiImage {
|
||||
image_type: "standard".to_string(),
|
||||
id: url_hash,
|
||||
url: image_url.clone(),
|
||||
cached_url: image_url,
|
||||
}
|
||||
primary: YesNoVal(true),
|
||||
data: mii_data,
|
||||
status: "COMPLETED".to_string(),
|
||||
mii_images: MiiImages {
|
||||
mii_image: {
|
||||
let image_url = get_mii_img_url(pid, "png");
|
||||
let url_hash = gxhash32(image_url.as_bytes(), 0);
|
||||
MiiImage {
|
||||
image_type: "standard".to_string(),
|
||||
id: url_hash,
|
||||
url: image_url.clone(),
|
||||
cached_url: image_url,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
off_device_flag: off_device_allowed,
|
||||
region,
|
||||
utc_offset: timezone_offset,
|
||||
account_level,
|
||||
}
|
||||
},
|
||||
off_device_flag: off_device_allowed,
|
||||
region,
|
||||
utc_offset: timezone_offset,
|
||||
account_level,
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/v1/api/people/@me/miis/@primary", data = "<data>")]
|
||||
|
|
@ -479,8 +481,8 @@ pub async fn change_mii(
|
|||
mii_data,
|
||||
pid
|
||||
)
|
||||
.execute(db)
|
||||
.await;
|
||||
.execute(db)
|
||||
.await;
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Failed to update Mii data for PID {}: {:?}", pid, e);
|
||||
|
|
@ -492,8 +494,5 @@ pub async fn change_mii(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[post("/v1/api/people/@me/agreements")]
|
||||
pub async fn thing(){
|
||||
|
||||
}
|
||||
pub async fn thing() {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#![deprecated = "handled by nex-account now"]
|
||||
/*
|
||||
use crate::Pool;
|
||||
|
||||
pub async fn next_pid(pool: &Pool) -> i32{
|
||||
loop {
|
||||
let next_pid = sqlx::query!("SELECT nextval('pid_counter') as pid")
|
||||
|
|
@ -26,4 +27,5 @@ pub async fn next_pid(pool: &Pool) -> i32{
|
|||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,69 +1,65 @@
|
|||
use std::net::Ipv4Addr;
|
||||
use rocket::{get, State};
|
||||
use serde::Serialize;
|
||||
use sqlx::types::ipnetwork::IpNetwork::V4;
|
||||
use crate::Pool;
|
||||
use crate::account::account::Auth;
|
||||
use crate::error::{Error, Errors};
|
||||
use crate::nnid::oauth::generate_token::{create_token};
|
||||
use crate::nnid::oauth::generate_token::create_token;
|
||||
use crate::nnid::oauth::generate_token::token_type::NEX_TOKEN;
|
||||
use crate::Pool;
|
||||
use crate::xml::Xml;
|
||||
use nex_account::grpc::Pid;
|
||||
use reqwest::header::SERVER;
|
||||
use rocket::{State, get};
|
||||
use serde::Serialize;
|
||||
use sqlx::types::ipnetwork::IpNetwork::V4;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
const NO_IPV4_ERROR: Errors = Errors{
|
||||
error: &[
|
||||
Error{
|
||||
code: "1022",
|
||||
message: "Server is not a valid IPv4 address"
|
||||
}
|
||||
]
|
||||
const NO_IPV4_ERROR: Errors = Errors {
|
||||
error: &[Error {
|
||||
code: "1022",
|
||||
message: "Server is not a valid IPv4 address",
|
||||
}],
|
||||
};
|
||||
|
||||
const SERVER_ERROR: Errors = Errors{
|
||||
error: &[
|
||||
Error{
|
||||
code: "9999",
|
||||
message: "Internal Server Error"
|
||||
}
|
||||
]
|
||||
const SERVER_ERROR: Errors = Errors {
|
||||
error: &[Error {
|
||||
code: "9999",
|
||||
message: "Internal Server Error",
|
||||
}],
|
||||
};
|
||||
|
||||
const NO_SERVER_ERROR: Errors = Errors{
|
||||
error: &[
|
||||
Error{
|
||||
code: "1021",
|
||||
message: "The requested game server was not found"
|
||||
}
|
||||
]
|
||||
const NO_SERVER_ERROR: Errors = Errors {
|
||||
error: &[Error {
|
||||
code: "1021",
|
||||
message: "The requested game server was not found",
|
||||
}],
|
||||
};
|
||||
|
||||
const MAINTENANCE_ERROR: Errors = Errors{
|
||||
error: &[
|
||||
Error{
|
||||
code: "2002",
|
||||
message: "The requested game server is under maintenance"
|
||||
}
|
||||
]
|
||||
const MAINTENANCE_ERROR: Errors = Errors {
|
||||
error: &[Error {
|
||||
code: "2002",
|
||||
message: "The requested game server is under maintenance",
|
||||
}],
|
||||
};
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename = "nex_token")]
|
||||
pub struct NexToken{
|
||||
pub struct NexToken {
|
||||
host: Ipv4Addr,
|
||||
nex_password: String,
|
||||
pid: i32,
|
||||
port: u16,
|
||||
token: String
|
||||
token: Box<str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename = "service_token")]
|
||||
pub struct ServiceToken{
|
||||
token: String
|
||||
pub struct ServiceToken {
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[get("/v1/api/provider/service_token/@me")]
|
||||
pub async fn get_service_token(pool: &State<Pool>, auth: Auth<true, false>) -> Result<Xml<ServiceToken>, Option<Errors<'static>>>{
|
||||
pub async fn get_service_token(
|
||||
pool: &State<Pool>,
|
||||
auth: Auth<true, false>,
|
||||
) -> Result<Xml<ServiceToken>, Option<Errors<'static>>> {
|
||||
// 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
|
||||
// that way we could reduce the data the database needs to store and also reduce the transfer
|
||||
|
|
@ -75,17 +71,15 @@ pub async fn get_service_token(pool: &State<Pool>, auth: Auth<true, false>) -> R
|
|||
|
||||
let token = create_token(pool, auth.pid, NEX_TOKEN, None).await;
|
||||
|
||||
Ok(
|
||||
Xml(
|
||||
ServiceToken{
|
||||
token
|
||||
}
|
||||
)
|
||||
)
|
||||
Ok(Xml(ServiceToken { token }))
|
||||
}
|
||||
|
||||
#[get("/v1/api/provider/nex_token/@me?<game_server_id>")]
|
||||
pub async fn get_nex_token(pool: &State<Pool>, auth: Auth<true, false>, game_server_id: &str) -> Result<Xml<NexToken>, Option<Errors<'static>>>{
|
||||
pub async fn get_nex_token(
|
||||
pool: &State<Pool>,
|
||||
auth: Auth<true, false>,
|
||||
game_server_id: &str,
|
||||
) -> Result<Xml<NexToken>, Option<Errors<'static>>> {
|
||||
// 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
|
||||
// that way we could reduce the data the database needs to store and also reduce the transfer
|
||||
|
|
@ -108,12 +102,10 @@ pub async fn get_nex_token(pool: &State<Pool>, auth: Auth<true, false>, game_ser
|
|||
Some(row) => row,
|
||||
None => return Err(Some(NO_SERVER_ERROR)),
|
||||
}; // only crash on db failure (not missing row)
|
||||
|
||||
if server.maintenance_mode {
|
||||
return Err(Some(MAINTENANCE_ERROR))
|
||||
}
|
||||
|
||||
let token = create_token(pool, auth.pid, NEX_TOKEN, None).await;
|
||||
if server.maintenance_mode {
|
||||
return Err(Some(MAINTENANCE_ERROR));
|
||||
}
|
||||
|
||||
let V4(host) = server.address else {
|
||||
return Err(Some(NO_IPV4_ERROR));
|
||||
|
|
@ -121,15 +113,26 @@ pub async fn get_nex_token(pool: &State<Pool>, auth: Auth<true, false>, game_ser
|
|||
|
||||
let host = host.ip();
|
||||
|
||||
Ok(
|
||||
Xml(
|
||||
NexToken{
|
||||
host,
|
||||
port: server.port as u16,
|
||||
nex_password: auth.nex_password.clone(),
|
||||
pid: auth.pid,
|
||||
token
|
||||
}
|
||||
)
|
||||
let mut client = nex_account::grpc_client().await.unwrap();
|
||||
let Ok(key) = client.get_nex_key_by_pid(Pid { pid: auth.pid }).await else {
|
||||
println!("account does not exist on nex-account server");
|
||||
return Err(Some(SERVER_ERROR));
|
||||
};
|
||||
|
||||
let token = nex_account::gen_nexact_token(
|
||||
auth.pid,
|
||||
key.into_inner()
|
||||
.key
|
||||
.try_into()
|
||||
.map_err(|_| Some(SERVER_ERROR))?,
|
||||
)
|
||||
.expect("NEX_ACCOUNT_KEYPAIR not set");
|
||||
|
||||
Ok(Xml(NexToken {
|
||||
host,
|
||||
port: server.port as u16,
|
||||
nex_password: auth.nex_password.clone(),
|
||||
pid: auth.pid,
|
||||
token,
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue