From fbebccc68cdc22e9ea416543ca0edef5644d9728 Mon Sep 17 00:00:00 2001 From: Kyler <59854022+KylerOlsen@users.noreply.github.com> Date: Tue, 23 Jan 2024 22:52:47 -0700 Subject: [PATCH] Added whitelist --- .gitignore | 1 + src/login_handle.rs | 152 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 142 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 6ce9b60..a6816e4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Cargo.lock test_server/* main_icon.png motd.json +whitelist.json diff --git a/src/login_handle.rs b/src/login_handle.rs index 6d08b7a..094d6b2 100644 --- a/src/login_handle.rs +++ b/src/login_handle.rs @@ -1,7 +1,12 @@ // Yeahbut December 2023 -use core::sync::atomic::{AtomicBool, Ordering}; +use std::fs; +use std::time::{Duration, Instant}; +use std::sync::{Arc, Mutex}; + use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use serde_json::Value; +use lazy_static::lazy_static; use purple_cello_mc_protocol::{ mc_types::{self, Result, Packet}, @@ -9,6 +14,14 @@ use purple_cello_mc_protocol::{ login, }; +const EXPIRATION_DURATION: Duration = Duration::from_secs(3600); + +struct CachedWhitelist { + whitelist_data: Value, + timestamp: Instant, +} + +#[derive(PartialEq)] struct Player { name: String, player_uuid: Option, @@ -19,17 +32,134 @@ enum PlayerAllowed { False(String), } -fn check_player(player: Player) -> Result { - static PARITY: AtomicBool = AtomicBool::new(true); - let parity: bool; - parity = PARITY.load(Ordering::Relaxed); - PARITY.store(!parity, Ordering::Relaxed); - if parity { - // if player.name.to_lowercase() == "yeahbut" { - Ok(PlayerAllowed::True(player)) - } else { - Ok(PlayerAllowed::False("Testing blocking, try again.".to_string())) +fn load_whitelist() -> Value { + let file_path = "./whitelist.json"; + + let data = match fs::read_to_string(file_path) { + Ok(data) => data, + Err(_) => return Value::Null, + }; + + let whitelist_data: Value = match serde_json::from_str(&data) { + Ok(value) => value, + Err(_) => return Value::Null, + }; + + whitelist_data +} + +fn get_whitelist() -> Vec { + lazy_static! { + static ref WHITELIST_CACHE: Arc>> = + Arc::new(Mutex::new(None)); } + + let mut cache = WHITELIST_CACHE.lock().unwrap(); + + if let Some(cached_whitelist) = cache.as_ref() { + if cached_whitelist.timestamp.elapsed() >= EXPIRATION_DURATION { + println!("Refreshing whitelist cache"); + *cache = Some(CachedWhitelist { + whitelist_data: load_whitelist(), + timestamp: Instant::now(), + }); + } + } else { + *cache = Some(CachedWhitelist { + whitelist_data: load_whitelist(), + timestamp: Instant::now(), + }); + } + + let whitelist_data = cache.as_ref().unwrap().whitelist_data.clone(); + + std::mem::drop(cache); + + if whitelist_data == Value::Null { + return Vec::new(); + } + + let whitelist_array = match whitelist_data.as_array() { + Some(whitelist) => whitelist, + None => { return Vec::new(); } + }; + + let mut whitelist: Vec = Vec::new(); + + for whitelisted_player in whitelist_array { + let player_map = match whitelisted_player.as_object() { + Some(whitelist) => whitelist, + None => { continue; } + }; + + let name = match player_map.get("name") { + Some(name) => { + match name.as_str() { + Some(name) => name, + None => { continue; } + } + }, + None => { continue; } + }; + + let player_uuid = match player_map.get("uuid") { + Some(uuid) => { + match uuid.as_str() { + Some(uuid) => { + match u128::from_str_radix(uuid, 16) { + Ok(uuid) => uuid, + Err(_) => { continue; } + } + }, + None => { continue; } + } + }, + None => { continue; } + }; + + whitelist.push(Player { + name: name.to_string(), + player_uuid: Some(player_uuid), + }) + } + + whitelist +} + +fn check_player_whitelist(player: Player) -> PlayerAllowed { + let whitelist = get_whitelist(); + + if player.player_uuid.is_none() { + return PlayerAllowed::False("Invalid UUID".to_string()); + } + + let mut invalid_uuid = false; + let mut invalid_username = false; + + for wl_player in whitelist { + if wl_player == player { + return PlayerAllowed::True(player); + } else if wl_player.name == player.name { + invalid_uuid = true; + } else if wl_player.player_uuid == player.player_uuid { + invalid_username = true; + } + } + + if invalid_uuid { + PlayerAllowed::False("Invalid UUID".to_string()) + } else if invalid_username { + PlayerAllowed::False( + "Invalid Username, please contact the server admin to update your \ + username: purplecelloserver@gmail.com".to_string() + ) + } else { + PlayerAllowed::False("Not whitelisted on this server".to_string()) + } +} + +fn check_player(player: Player) -> Result { + Ok(check_player_whitelist(player)) } pub async fn respond_login(