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 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::<Player>().get(entity) {
let mut accounts = state.ecs().write_resource::<AuthProvider>();
accounts.logout(player.uuid());
let mut login_provider = state.ecs().write_resource::<LoginProvider>();
login_provider.logout(player.uuid());
let msg = comp::ChatType::Offline.server_msg(format!("[{}] went offline.", &player.alias));
state.notify_registered_clients(msg);

View File

@ -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::<ServerEvent>::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

View File

@ -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<Uuid, String>,
auth_server: Option<AuthClient>,
whitelist: Vec<String>,
}
impl AuthProvider {
pub fn new(auth_addr: Option<String>, whitelist: Vec<String>) -> Self {
impl LoginProvider {
pub fn new(auth_addr: Option<String>) -> 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))
},
}
}

View File

@ -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>,