mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add user registration
This commit is contained in:
@@ -259,6 +259,7 @@ impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
|
||||
fn server_config(&self) -> super::ServerConfig {
|
||||
super::ServerConfig {
|
||||
database: self.settings.server.database.clone(),
|
||||
auto_signup: self.settings.server.auto_signup,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ pub struct TypedDevMessage<C> {
|
||||
|
||||
pub struct ServerConfig {
|
||||
pub database: String,
|
||||
pub auto_signup: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -72,6 +72,8 @@ fn default_slot_upgrades() -> Vec<GarageSlotUpgrade> {
|
||||
pub struct ServerSettings {
|
||||
#[serde(default = "default_db_conn")]
|
||||
pub database: String,
|
||||
#[serde(default)]
|
||||
pub auto_signup: bool,
|
||||
}
|
||||
|
||||
fn default_db_conn() -> String {
|
||||
@@ -81,5 +83,6 @@ fn default_db_conn() -> String {
|
||||
fn default_server_conf() -> ServerSettings {
|
||||
ServerSettings {
|
||||
database: default_db_conn(),
|
||||
auto_signup: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::persist::config::ConfigProvider;
|
||||
pub struct AccountProvider {
|
||||
cubes: std::sync::Arc<Vec<u32>>,
|
||||
garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
||||
auto_signups: bool,
|
||||
secret: Vec<u8>,
|
||||
db: std::sync::Arc<rc_database::Database>,
|
||||
}
|
||||
@@ -12,13 +13,15 @@ pub struct AccountProvider {
|
||||
impl AccountProvider {
|
||||
pub async fn load(root: impl AsRef<std::path::Path>, conf: &crate::persist::config::ConfigImpl) -> std::io::Result<Self> {
|
||||
let token_path = root.as_ref().join(super::TOKEN_SECRET_FILENAME);
|
||||
let database_uri = <crate::persist::config::ConfigImpl as ConfigProvider<()>>::server_config(conf).database;
|
||||
let server_settings = <crate::persist::config::ConfigImpl as ConfigProvider<()>>::server_config(conf);
|
||||
let database_uri = server_settings.database;
|
||||
log::debug!("Connecting to user database URI: {}", database_uri);
|
||||
let db = rc_database::Database::init(&database_uri).await
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotConnected, e))?;
|
||||
Ok(Self {
|
||||
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(conf)),
|
||||
garage_upgrades: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::garage_upgrades(conf)),
|
||||
auto_signups: server_settings.auto_signup,
|
||||
secret: std::fs::read(&token_path)?,
|
||||
db: std::sync::Arc::new(db),
|
||||
})
|
||||
@@ -72,9 +75,14 @@ impl super::UserAuthenticator for AccountProvider {
|
||||
user_info
|
||||
} else {
|
||||
is_new_user = true;
|
||||
log::info!("New user {}", info.payload.public_id);
|
||||
super::setup_new_user(&info, &self.db).await.map_err(|e| e.to_string())?;
|
||||
self.db.user_by_display_name(info.payload.display_name.clone()).await.map_err(|e| e.to_string())?.unwrap()
|
||||
if self.auto_signups {
|
||||
log::info!("New user {}", info.payload.public_id);
|
||||
super::setup_new_user(&info, &self.db).await.map_err(|e| e.to_string())?;
|
||||
self.db.user_by_display_name(info.payload.display_name.clone()).await.map_err(|e| e.to_string())?.unwrap()
|
||||
} else {
|
||||
log::info!("Rejecting user sign-in for `{}` (set settings.server.auto_signup=true to disable this behaviour)", info.payload.public_id);
|
||||
return Err(format!("User does not exist"));
|
||||
}
|
||||
};
|
||||
let override_password = user_info.password.is_empty() && user_info.steam_id.is_none();
|
||||
match info.extra {
|
||||
@@ -128,6 +136,24 @@ impl super::UserAuthenticator for AccountProvider {
|
||||
is_new: is_new_user,
|
||||
})
|
||||
}
|
||||
|
||||
async fn user_exists(&self, user: super::UserId) -> Result<bool, String> {
|
||||
Ok(match user {
|
||||
super::UserId::SteamId(steam_id) => {
|
||||
self.db.user_by_steam_id(steam_id).await.map_err(|e| e.to_string())?.is_some()
|
||||
},
|
||||
super::UserId::Email(email) => {
|
||||
self.db.user_by_email(email).await.map_err(|e| e.to_string())?.is_some()
|
||||
},
|
||||
super::UserId::Username(display_name) => {
|
||||
self.db.user_by_display_name(display_name).await.map_err(|e| e.to_string())?.is_some()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn register(&self, info: super::RegistrationInfo) -> Result<u32, String> {
|
||||
super::register_new_user(&info, &self.db).await.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -5,17 +5,17 @@ fn current_unix_time() -> i64 {
|
||||
}
|
||||
|
||||
async fn build_new_account_data(user: &super::UserInfo, db: &rc_database::Database) -> Result<(), rc_database::sea_orm::DbErr> {
|
||||
//std::fs::create_dir(&root)?;
|
||||
//let garage_dir = root.as_ref().join(super::GARAGE_DIR);
|
||||
//std::fs::create_dir(&garage_dir)?;
|
||||
let user_data = db.insert_user(default_user_data(user)).await?;
|
||||
db.insert_perms(default_user_perms(user_data.id)).await?;
|
||||
db.insert_user_aux(default_user_aux_data(user_data.id)).await?;
|
||||
db.insert_garages(default_garage_slots(user_data.id)).await?;
|
||||
/*for slot in default_garage_slots() {
|
||||
let filepath = garage_dir.join(format!("{}.json", slot.slot));
|
||||
slot.save(filepath)?;
|
||||
}*/
|
||||
let reg_info = super::RegistrationInfo {
|
||||
display_name: user.payload.display_name.clone(),
|
||||
password: if let super::ExtraUserInfo::Email { password } | super::ExtraUserInfo::Username { password } = &user.extra {
|
||||
password.to_owned()
|
||||
} else {
|
||||
"".to_owned()
|
||||
},
|
||||
email: None,
|
||||
steam_id: if let super::ExtraUserInfo::Steam { id } = &user.extra { Some(*id) } else { None },
|
||||
};
|
||||
register_new_user(®_info, db).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -31,23 +31,28 @@ pub async fn setup_new_user(user: &super::UserInfo, db: &rc_database::Database)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_user_data(user: &super::UserInfo) -> rc_database::schema::user::ActiveModel {
|
||||
let password = if let super::ExtraUserInfo::Email { password } | super::ExtraUserInfo::Username { password } = &user.extra {
|
||||
//password.to_owned()
|
||||
pub async fn register_new_user(info: &super::RegistrationInfo, db: &rc_database::Database) -> Result<u32, rc_database::sea_orm::DbErr> {
|
||||
let user_data = db.insert_user(default_user_data(info)).await?;
|
||||
db.insert_perms(default_user_perms(user_data.id)).await?;
|
||||
db.insert_user_aux(default_user_aux_data(user_data.id)).await?;
|
||||
db.insert_garages(default_garage_slots(user_data.id)).await?;
|
||||
Ok(user_data.id)
|
||||
}
|
||||
|
||||
fn default_user_data(info: &super::RegistrationInfo) -> rc_database::schema::user::ActiveModel {
|
||||
let password = {
|
||||
use argon2::password_hash::PasswordHasher;
|
||||
let argon2_algo = argon2::Argon2::default();
|
||||
let salt = argon2::password_hash::SaltString::generate(&mut argon2::password_hash::rand_core::OsRng);
|
||||
match argon2_algo.hash_password(password.as_bytes(), &salt) {
|
||||
match argon2_algo.hash_password(info.password.as_bytes(), &salt) {
|
||||
Err(e) => {
|
||||
log::error!("Failed to hash password for user {}/{}: {}", user.payload.public_id, user.payload.display_name, e);
|
||||
log::error!("Failed to hash password for user {}: {}", info.display_name, e);
|
||||
"".to_owned()
|
||||
},
|
||||
Ok(password) => password.to_string(),
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let steam_id = if let super::ExtraUserInfo::Steam { id } = &user.extra {
|
||||
let steam_id = if let Some(id) = info.steam_id {
|
||||
Some(id.to_string())
|
||||
} else {
|
||||
None
|
||||
@@ -55,28 +60,12 @@ fn default_user_data(user: &super::UserInfo) -> rc_database::schema::user::Activ
|
||||
rc_database::schema::user::ActiveModel {
|
||||
id: Default::default(),
|
||||
creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()),
|
||||
public_id: rc_database::sea_orm::ActiveValue::Set(user.payload.public_id.clone()),
|
||||
display_name: rc_database::sea_orm::ActiveValue::Set(user.payload.display_name.clone()),
|
||||
public_id: rc_database::sea_orm::ActiveValue::Set(info.display_name.clone()),
|
||||
display_name: rc_database::sea_orm::ActiveValue::Set(info.display_name.clone()),
|
||||
password: rc_database::sea_orm::ActiveValue::Set(password),
|
||||
email: rc_database::sea_orm::ActiveValue::Set("//TODO".to_owned()),
|
||||
email: rc_database::sea_orm::ActiveValue::Set(info.email.clone().unwrap_or_else(|| "".to_owned())),
|
||||
steam_id: rc_database::sea_orm::ActiveValue::Set(steam_id),
|
||||
}
|
||||
|
||||
/*super::AccountInfo {
|
||||
is_mod: false,
|
||||
is_admin: false,
|
||||
is_dev: false,
|
||||
steam_id: None,
|
||||
password: None,
|
||||
inventory: super::UnlockedParts {
|
||||
unlocked: vec![],
|
||||
override_: super::inventory::UnlockOverride::UnlockAll,
|
||||
},
|
||||
garage: super::SelectedGarage {
|
||||
uuid: (0, 0),
|
||||
slot: 0,
|
||||
},
|
||||
}*/
|
||||
}
|
||||
|
||||
fn default_user_aux_data(user_id: u32) -> Vec<rc_database::schema::user_aux::ActiveModel> {
|
||||
|
||||
@@ -5,13 +5,13 @@ mod garage_data;
|
||||
pub use garage_data::SelectedGarage;
|
||||
|
||||
mod initial_data;
|
||||
pub use initial_data::setup_new_user;
|
||||
pub use initial_data::{setup_new_user, register_new_user};
|
||||
|
||||
mod inventory;
|
||||
pub use inventory::UnlockedParts;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData};
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo};
|
||||
|
||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||
|
||||
|
||||
@@ -23,11 +23,24 @@ pub enum ExtraUserInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum UserId {
|
||||
SteamId(u64),
|
||||
Email(String),
|
||||
Username(String),
|
||||
}
|
||||
|
||||
pub struct UserLoginInfo {
|
||||
pub response: libfj::robocraft::AuthenticationResponseInfo,
|
||||
pub is_new: bool,
|
||||
}
|
||||
|
||||
pub struct RegistrationInfo {
|
||||
pub display_name: String,
|
||||
pub password: String,
|
||||
pub email: Option<String>,
|
||||
pub steam_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait UserProvider<C> {
|
||||
async fn authenticate(&self, user: UserToken, ext: std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync + 'static>>) -> Result<Box<dyn User<C> + Send + Sync>, String>;
|
||||
@@ -36,6 +49,8 @@ pub trait UserProvider<C> {
|
||||
#[async_trait::async_trait]
|
||||
pub trait UserAuthenticator {
|
||||
async fn login(&self, info: UserInfo) -> Result<UserLoginInfo, String>;
|
||||
async fn user_exists(&self, user: UserId) -> Result<bool, String>;
|
||||
async fn register(&self, info: RegistrationInfo) -> Result<u32, String>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
Reference in New Issue
Block a user