From 080466200060d2d3ec64bec32a4959fa061b79ce Mon Sep 17 00:00:00 2001 From: troido Date: Thu, 23 Apr 2020 12:53:01 +0200 Subject: accept authentication messages, and validate registrations --- src/gameserver.rs | 109 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 16 deletions(-) (limited to 'src/gameserver.rs') diff --git a/src/gameserver.rs b/src/gameserver.rs index c27d5d4..195989f 100644 --- a/src/gameserver.rs +++ b/src/gameserver.rs @@ -9,13 +9,20 @@ use unicode_categories::UnicodeCategories; use crate::{ controls::{Control, Action}, server::Server, - PlayerId + PlayerId, + auth::{UserRegistry, LoaderError} }; +#[derive(Debug, Clone, PartialEq)] +enum Authentication { + Guest, + Tilde, + Passtoken(String) +} #[derive(Debug)] enum Message { - Name(String), + Auth(String, Authentication), Chat(String), Input(Value) } @@ -36,15 +43,17 @@ macro_rules! merr { pub struct GameServer { players: HashMap<(usize, usize), PlayerId>, connections: HashMap, + users: Box, servers: Vec> } impl GameServer { - pub fn new(servers: Vec>) -> GameServer { + pub fn new(servers: Vec>, users: Box) -> GameServer { GameServer { players: HashMap::new(), connections: HashMap::new(), - servers + servers, + users } } @@ -120,19 +129,14 @@ impl GameServer { fn handle_message(&mut self, (serverid, connectionid): (usize, usize), msg: Message) -> Result, MessageError> { let id = (serverid, connectionid); match msg { - Message::Name(name) => { + Message::Auth(name, auth) => { if name.len() > 99 { return Err(merr!(name, "A name can not be longer than 99 bytes")); } if name.len() == 0 { return Err(merr!(name, "A name must have at least one character")); } - let (firstchar, username) = name.split_at(1); - if firstchar == "~" { - if Some(username.to_string()) != self.servers[serverid].get_name(connectionid) { - return Err(merr!(name, "A tilde name must match your username")); - } - } else { + if auth != Authentication::Tilde { for chr in name.chars() { if !(chr.is_letter() || chr.is_number() || chr.is_punctuation_connector()){ return Err(merr!(name, "A name can only contain letters, numbers and underscores")); @@ -143,16 +147,20 @@ impl GameServer { return Err(merr!(action, "You can not change your name")); } let player = PlayerId{name}; + self.authenticate(&player, auth, id)?; if self.connections.contains_key(&player) { return Err(merr!("nametaken", "Another connection to this player exists already")); } self.broadcast_message(&format!("{} connected", player.name)); self.players.insert(id, player.clone()); self.connections.insert(player.clone(), id); + if let Err(_) = self.send(&player, json!(["connected", format!("successfully connected as {}", &player.name)])){ + return Err(merr!("server", "unable to send connected message")) + } Ok(Some(Action::Join(player))) } Message::Chat(text) => { - let player = self.players.get(&id).ok_or(merr!(action, "Set a name before you send any other messages"))?; + let player = self.players.get(&id).ok_or(merr!(action, "Set a valid name before you send any other messages"))?; let name = player.name.clone(); self.broadcast_message(&format!("{}: {}", name, text)); Ok(None) @@ -164,6 +172,47 @@ impl GameServer { } } } + + fn authenticate(&self, player: &PlayerId, auth: Authentication, (serverid, connectionid): (usize, usize)) -> Result<(), MessageError> { + Ok(match auth { + Authentication::Guest => { + if self.users.user_exists(&player) { + return Err(merr!("registered", "This name is registered. Use another name or authenticate for this name")) + } + () + } + Authentication::Tilde => { + let (firstchar, username) = player.name.split_at(1); + if firstchar == "~" { + if Some(username.to_string()) != self.servers[serverid].get_name(connectionid) { + return Err(merr!(name, "A tilde name must match your username")); + } + } + } + Authentication::Passtoken(token) => { + match self.users.load_user(player) { + Ok(user) => { + if player.name != user.name { + println!("Name mismatch: user entry for {:?} has name {}", player, user.name); + return Err(merr!("server", "name mismatch")); + } + if token != user.pass_token { + println!("password mismatch: '{}' '{}'", token, user.pass_token); + return Err(merr!("invalidtoken", "invalid pass token")); + } + () + } + Err(LoaderError::InvalidResource(err)) => { + println!("failed to load user data for user '{}': {}", player.name, err); + return Err(merr!("server", "failed to load user data")) + } + Err(LoaderError::MissingResource(_)) => { + return Err(merr!("unregistered", "this name is not registered")) + } + } + } + }) + } } @@ -174,18 +223,46 @@ fn parse_message(msg: &str) -> Result { if arr.len() < 2 { return Err(merr!(msg, "array not long enough")); } + let arg = &arr[1]; let msgtype = arr[0].as_str().ok_or(merr!(msg, "first message element not a string"))?; Ok(match msgtype { "name" => { - let name = arr[1].as_str().ok_or(merr!(msg, "name not a string"))?; - Message::Name(name.to_string()) + let name = arg.as_str().ok_or(merr!(msg, "name not a string"))?.to_string(); + Message::Auth( + name.clone(), + if name.starts_with("~") { + Authentication::Tilde + } else { + Authentication::Guest + } + ) } "chat" => { - let text = arr[1].as_str().ok_or(merr!(msg, "chat text not a string"))?; + let text = arg.as_str().ok_or(merr!(msg, "chat text not a string"))?; Message::Chat(text.escape_debug().to_string()) } "input" => { - Message::Input(arr[1].clone()) + Message::Input(arg.clone()) + } + "auth" => { + let name = arg.get("name").ok_or(merr!(msg, "auth message does not have name"))?.as_str().ok_or(merr!(msg, "auth name not a string"))?.to_string(); + let typ = arg.get("type").ok_or(merr!(msg, "auth message does not have type"))?.as_str().ok_or(merr!(msg, "auth type not a string"))?; + Message::Auth( + name, + match typ { + "guest" => Authentication::Guest, + "tilde" => Authentication::Tilde, + "passtoken" => Authentication::Passtoken( + arg + .get("passtoken") + .ok_or(merr!(msg, "passtoken auth message does not have passtoken"))? + .as_str() + .ok_or(merr!(msg, "passtoken not a string"))? + .to_string() + ), + _ => {return Err(merr!(msg, "invalid authentication type"))} + } + ) } _ => { return Err(merr!(msg, format!("unknown messsage type {:?}", msgtype))) -- cgit