fix #660; Admins should join even if they aren't on whitelist

This commit is contained in:
lausek 2020-07-23 17:16:52 +00:00 committed by Marcel
parent c903e27160
commit f7a3a622e5
4 changed files with 61 additions and 47 deletions

View File

@ -1,6 +1,6 @@
use super::Event; use super::Event;
use crate::{ use crate::{
auth_provider::AuthProvider, client::Client, persistence, state_ext::StateExt, Server, client::Client, login_provider::LoginProvider, persistence, state_ext::StateExt, Server,
}; };
use common::{ use common::{
comp, 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))) 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 // And send a disconnected message
if let Some(player) = state.ecs().read_storage::<Player>().get(entity) { if let Some(player) = state.ecs().read_storage::<Player>().get(entity) {
let mut accounts = state.ecs().write_resource::<AuthProvider>(); let mut login_provider = state.ecs().write_resource::<LoginProvider>();
accounts.logout(player.uuid()); login_provider.logout(player.uuid());
let msg = comp::ChatType::Offline.server_msg(format!("[{}] went offline.", &player.alias)); let msg = comp::ChatType::Offline.server_msg(format!("[{}] went offline.", &player.alias));
state.notify_registered_clients(msg); state.notify_registered_clients(msg);

View File

@ -3,13 +3,13 @@
#![feature(drain_filter, option_zip)] #![feature(drain_filter, option_zip)]
pub mod alias_validator; pub mod alias_validator;
pub mod auth_provider;
pub mod chunk_generator; pub mod chunk_generator;
pub mod client; pub mod client;
pub mod cmd; pub mod cmd;
pub mod error; pub mod error;
pub mod events; pub mod events;
pub mod input; pub mod input;
pub mod login_provider;
pub mod metrics; pub mod metrics;
pub mod persistence; pub mod persistence;
pub mod settings; pub mod settings;
@ -22,10 +22,10 @@ pub use crate::{error::Error, events::Event, input::Input, settings::ServerSetti
use crate::{ use crate::{
alias_validator::AliasValidator, alias_validator::AliasValidator,
auth_provider::AuthProvider,
chunk_generator::ChunkGenerator, chunk_generator::ChunkGenerator,
client::{Client, RegionSubscription}, client::{Client, RegionSubscription},
cmd::ChatCommandExt, cmd::ChatCommandExt,
login_provider::LoginProvider,
state_ext::StateExt, state_ext::StateExt,
sys::sentinel::{DeletedEntities, TrackedComps}, sys::sentinel::{DeletedEntities, TrackedComps},
}; };
@ -99,10 +99,9 @@ impl Server {
let mut state = State::default(); let mut state = State::default();
state.ecs_mut().insert(settings.clone()); state.ecs_mut().insert(settings.clone());
state.ecs_mut().insert(EventBus::<ServerEvent>::default()); state.ecs_mut().insert(EventBus::<ServerEvent>::default());
state.ecs_mut().insert(AuthProvider::new( state
settings.auth_server_address.clone(), .ecs_mut()
settings.whitelist.clone(), .insert(LoginProvider::new(settings.auth_server_address.clone()));
));
state.ecs_mut().insert(Tick(0)); state.ecs_mut().insert(Tick(0));
state.ecs_mut().insert(ChunkGenerator::new()); state.ecs_mut().insert(ChunkGenerator::new());
state state

View File

@ -15,33 +15,64 @@ fn derive_uuid(username: &str) -> Uuid {
Uuid::from_slice(&state.to_be_bytes()).unwrap() Uuid::from_slice(&state.to_be_bytes()).unwrap()
} }
pub struct AuthProvider { pub struct LoginProvider {
accounts: HashMap<Uuid, String>, accounts: HashMap<Uuid, String>,
auth_server: Option<AuthClient>, auth_server: Option<AuthClient>,
whitelist: Vec<String>,
} }
impl AuthProvider { impl LoginProvider {
pub fn new(auth_addr: Option<String>, whitelist: Vec<String>) -> Self { pub fn new(auth_addr: Option<String>) -> Self {
let auth_server = match auth_addr { let auth_server = match auth_addr {
Some(addr) => Some(AuthClient::new(addr)), Some(addr) => Some(AuthClient::new(addr)),
None => None, None => None,
}; };
AuthProvider { Self {
accounts: HashMap::new(), accounts: HashMap::new(),
auth_server, 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) { pub fn logout(&mut self, uuid: Uuid) {
if self.accounts.remove(&uuid).is_none() { if self.accounts.remove(&uuid).is_none() {
error!(?uuid, "Attempted to logout user that is not logged in."); 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 // Based on whether auth server is provided or not we expect an username or
// token // token
match &self.auth_server { match &self.auth_server {
@ -49,36 +80,19 @@ impl AuthProvider {
Some(srv) => { Some(srv) => {
info!(?username_or_token, "Validating token"); info!(?username_or_token, "Validating token");
// Parse 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()))?; .map_err(|e| RegisterError::AuthError(e.to_string()))?;
// Validate token // Validate token
let uuid = srv.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)?; 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)) Ok((username, uuid))
}, },
// Username is expected // Username is expected
None => { None => {
// Assume username was provided // Assume username was provided
let username = username_or_token; let username = username_or_token;
let uuid = derive_uuid(&username); let uuid = derive_uuid(username);
if !self.accounts.contains_key(&uuid) { Ok((username.to_string(), uuid))
info!(?username, "New User");
self.accounts.insert(uuid, username.clone());
Ok((username, uuid))
} else {
Err(RegisterError::AlreadyLoggedIn)
}
}, },
} }
} }

View File

@ -1,6 +1,6 @@
use super::SysTimer; use super::SysTimer;
use crate::{ use crate::{
alias_validator::AliasValidator, auth_provider::AuthProvider, client::Client, alias_validator::AliasValidator, client::Client, login_provider::LoginProvider,
persistence::character::CharacterLoader, ServerSettings, CLIENT_TIMEOUT, persistence::character::CharacterLoader, ServerSettings, CLIENT_TIMEOUT,
}; };
use common::{ use common::{
@ -45,7 +45,7 @@ impl Sys {
force_updates: &ReadStorage<'_, ForceUpdate>, force_updates: &ReadStorage<'_, ForceUpdate>,
stats: &mut WriteStorage<'_, Stats>, stats: &mut WriteStorage<'_, Stats>,
chat_modes: &ReadStorage<'_, ChatMode>, chat_modes: &ReadStorage<'_, ChatMode>,
accounts: &mut WriteExpect<'_, AuthProvider>, login_provider: &mut WriteExpect<'_, LoginProvider>,
block_changes: &mut Write<'_, BlockChange>, block_changes: &mut Write<'_, BlockChange>,
admin_list: &ReadExpect<'_, AdminList>, admin_list: &ReadExpect<'_, AdminList>,
admins: &mut WriteStorage<'_, Admin>, admins: &mut WriteStorage<'_, Admin>,
@ -86,7 +86,8 @@ impl Sys {
view_distance, view_distance,
token_or_username, token_or_username,
} => { } => {
let (username, uuid) = match accounts.query(token_or_username.clone()) { let (username, uuid) =
match login_provider.try_login(&token_or_username, &settings.whitelist) {
Err(err) => { Err(err) => {
client.error_state(RequestStateError::RegisterDenied(err)); client.error_state(RequestStateError::RegisterDenied(err));
break Ok(()); break Ok(());
@ -411,7 +412,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, ForceUpdate>, ReadStorage<'a, ForceUpdate>,
WriteStorage<'a, Stats>, WriteStorage<'a, Stats>,
ReadStorage<'a, ChatMode>, ReadStorage<'a, ChatMode>,
WriteExpect<'a, AuthProvider>, WriteExpect<'a, LoginProvider>,
Write<'a, BlockChange>, Write<'a, BlockChange>,
ReadExpect<'a, AdminList>, ReadExpect<'a, AdminList>,
WriteStorage<'a, Admin>, WriteStorage<'a, Admin>,