diff --git a/server/src/events/player.rs b/server/src/events/player.rs index b44f61d7f4..0e7bd7120d 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -1,6 +1,6 @@ use super::Event; use crate::{ - auth_provider::AuthProvider, client::Client, persistence, state_ext::StateExt, Server, + client::Client, login_provider::LoginProvider, persistence, state_ext::StateExt, Server, }; use common::{ comp, @@ -68,11 +68,11 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event state.notify_registered_clients(ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(*uid))) } - // Make sure to remove the player from the logged in list. (See AuthProvider) + // Make sure to remove the player from the logged in list. (See LoginProvider) // And send a disconnected message if let Some(player) = state.ecs().read_storage::().get(entity) { - let mut accounts = state.ecs().write_resource::(); - accounts.logout(player.uuid()); + let mut login_provider = state.ecs().write_resource::(); + login_provider.logout(player.uuid()); let msg = comp::ChatType::Offline.server_msg(format!("[{}] went offline.", &player.alias)); state.notify_registered_clients(msg); diff --git a/server/src/lib.rs b/server/src/lib.rs index c37fdb9708..2f5fff5ae9 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -3,13 +3,13 @@ #![feature(drain_filter, option_zip)] pub mod alias_validator; -pub mod auth_provider; pub mod chunk_generator; pub mod client; pub mod cmd; pub mod error; pub mod events; pub mod input; +pub mod login_provider; pub mod metrics; pub mod persistence; pub mod settings; @@ -22,10 +22,10 @@ pub use crate::{error::Error, events::Event, input::Input, settings::ServerSetti use crate::{ alias_validator::AliasValidator, - auth_provider::AuthProvider, chunk_generator::ChunkGenerator, client::{Client, RegionSubscription}, cmd::ChatCommandExt, + login_provider::LoginProvider, state_ext::StateExt, sys::sentinel::{DeletedEntities, TrackedComps}, }; @@ -99,10 +99,9 @@ impl Server { let mut state = State::default(); state.ecs_mut().insert(settings.clone()); state.ecs_mut().insert(EventBus::::default()); - state.ecs_mut().insert(AuthProvider::new( - settings.auth_server_address.clone(), - settings.whitelist.clone(), - )); + state + .ecs_mut() + .insert(LoginProvider::new(settings.auth_server_address.clone())); state.ecs_mut().insert(Tick(0)); state.ecs_mut().insert(ChunkGenerator::new()); state diff --git a/server/src/auth_provider.rs b/server/src/login_provider.rs similarity index 54% rename from server/src/auth_provider.rs rename to server/src/login_provider.rs index 9b2c944f61..0410f528c7 100644 --- a/server/src/auth_provider.rs +++ b/server/src/login_provider.rs @@ -15,33 +15,64 @@ fn derive_uuid(username: &str) -> Uuid { Uuid::from_slice(&state.to_be_bytes()).unwrap() } -pub struct AuthProvider { +pub struct LoginProvider { accounts: HashMap, auth_server: Option, - whitelist: Vec, } -impl AuthProvider { - pub fn new(auth_addr: Option, whitelist: Vec) -> Self { +impl LoginProvider { + pub fn new(auth_addr: Option) -> Self { let auth_server = match auth_addr { Some(addr) => Some(AuthClient::new(addr)), None => None, }; - AuthProvider { + Self { accounts: HashMap::new(), auth_server, - whitelist, } } + fn login(&mut self, uuid: Uuid, username: String) -> Result<(), RegisterError> { + // make sure that the user is not logged in already + if self.accounts.contains_key(&uuid) { + return Err(RegisterError::AlreadyLoggedIn); + } + info!(?username, "New User"); + self.accounts.insert(uuid, username); + Ok(()) + } + pub fn logout(&mut self, uuid: Uuid) { if self.accounts.remove(&uuid).is_none() { error!(?uuid, "Attempted to logout user that is not logged in."); }; } - pub fn query(&mut self, username_or_token: String) -> Result<(String, Uuid), RegisterError> { + pub fn try_login( + &mut self, + username_or_token: &str, + whitelist: &[String], + ) -> Result<(String, Uuid), RegisterError> { + self + // resolve user information + .query(username_or_token) + // if found, check name against whitelist or if user is admin + .and_then(|(username, uuid)| { + // user can only join if he is admin, the whitelist is empty (everyone can join) + // or his name is in the whitelist + if !whitelist.is_empty() && !whitelist.contains(&username) { + return Err(RegisterError::NotOnWhitelist); + } + + // add the user to self.accounts + self.login(uuid, username.clone())?; + + Ok((username, uuid)) + }) + } + + pub fn query(&mut self, username_or_token: &str) -> Result<(String, Uuid), RegisterError> { // Based on whether auth server is provided or not we expect an username or // token match &self.auth_server { @@ -49,36 +80,19 @@ impl AuthProvider { Some(srv) => { info!(?username_or_token, "Validating token"); // Parse token - let token = AuthToken::from_str(&username_or_token) + let token = AuthToken::from_str(username_or_token) .map_err(|e| RegisterError::AuthError(e.to_string()))?; // Validate token let uuid = srv.validate(token)?; - // Check if already logged in - if self.accounts.contains_key(&uuid) { - return Err(RegisterError::AlreadyLoggedIn); - } let username = srv.uuid_to_username(uuid)?; - // Check if player is in whitelist - if self.whitelist.len() > 0 && !self.whitelist.contains(&username) { - return Err(RegisterError::NotOnWhitelist); - } - - // Log in - self.accounts.insert(uuid, username.clone()); Ok((username, uuid)) }, // Username is expected None => { // Assume username was provided let username = username_or_token; - let uuid = derive_uuid(&username); - if !self.accounts.contains_key(&uuid) { - info!(?username, "New User"); - self.accounts.insert(uuid, username.clone()); - Ok((username, uuid)) - } else { - Err(RegisterError::AlreadyLoggedIn) - } + let uuid = derive_uuid(username); + Ok((username.to_string(), uuid)) }, } } diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 625b57c7d8..d2d7c895d6 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -1,6 +1,6 @@ use super::SysTimer; use crate::{ - alias_validator::AliasValidator, auth_provider::AuthProvider, client::Client, + alias_validator::AliasValidator, client::Client, login_provider::LoginProvider, persistence::character::CharacterLoader, ServerSettings, CLIENT_TIMEOUT, }; use common::{ @@ -45,7 +45,7 @@ impl Sys { force_updates: &ReadStorage<'_, ForceUpdate>, stats: &mut WriteStorage<'_, Stats>, chat_modes: &ReadStorage<'_, ChatMode>, - accounts: &mut WriteExpect<'_, AuthProvider>, + login_provider: &mut WriteExpect<'_, LoginProvider>, block_changes: &mut Write<'_, BlockChange>, admin_list: &ReadExpect<'_, AdminList>, admins: &mut WriteStorage<'_, Admin>, @@ -86,13 +86,14 @@ impl Sys { view_distance, token_or_username, } => { - let (username, uuid) = match accounts.query(token_or_username.clone()) { - Err(err) => { - client.error_state(RequestStateError::RegisterDenied(err)); - break Ok(()); - }, - Ok((username, uuid)) => (username, uuid), - }; + let (username, uuid) = + match login_provider.try_login(&token_or_username, &settings.whitelist) { + Err(err) => { + client.error_state(RequestStateError::RegisterDenied(err)); + break Ok(()); + }, + Ok((username, uuid)) => (username, uuid), + }; let vd = view_distance.map(|vd| vd.min(settings.max_view_distance.unwrap_or(vd))); @@ -411,7 +412,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, ForceUpdate>, WriteStorage<'a, Stats>, ReadStorage<'a, ChatMode>, - WriteExpect<'a, AuthProvider>, + WriteExpect<'a, LoginProvider>, Write<'a, BlockChange>, ReadExpect<'a, AdminList>, WriteStorage<'a, Admin>,