diff --git a/server/src/alias_validator.rs b/server/src/alias_validator.rs deleted file mode 100644 index 7eb27ea3f3..0000000000 --- a/server/src/alias_validator.rs +++ /dev/null @@ -1,139 +0,0 @@ -use common::character::MAX_NAME_LENGTH; -use std::fmt::{self, Display}; - -#[derive(Debug, Default)] -pub struct AliasValidator { - banned_substrings: Vec, -} - -impl AliasValidator { - pub fn new(banned_substrings: Vec) -> Self { - let banned_substrings = banned_substrings - .iter() - .map(|string| string.to_lowercase()) - .collect(); - - AliasValidator { banned_substrings } - } - - pub fn validate(&self, alias: &str) -> Result<(), ValidatorError> { - if alias.len() > MAX_NAME_LENGTH { - return Err(ValidatorError::TooLong(alias.to_owned(), alias.len())); - } - - let lowercase_alias = alias.to_lowercase(); - - for banned_word in self.banned_substrings.iter() { - if lowercase_alias.contains(banned_word) { - return Err(ValidatorError::Forbidden( - alias.to_owned(), - banned_word.to_owned(), - )); - } - } - Ok(()) - } -} - -#[derive(Debug, PartialEq)] -pub enum ValidatorError { - Forbidden(String, String), - TooLong(String, usize), -} - -impl Display for ValidatorError { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Forbidden(name, _) => write!( - formatter, - "Character name \"{}\" contains a banned word", - name - ), - Self::TooLong(name, _) => write!(formatter, "Character name \"{}\" too long", name), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn multiple_matches() { - let banned_substrings = vec!["bad".to_owned(), "worse".to_owned()]; - let validator = AliasValidator::new(banned_substrings); - - let bad_alias = "BadplayerMcWorseFace"; - let result = validator.validate(bad_alias); - - assert_eq!( - result, - Err(ValidatorError::Forbidden( - bad_alias.to_owned(), - "bad".to_owned() - )) - ); - } - - #[test] - fn single_lowercase_match() { - let banned_substrings = vec!["blue".to_owned()]; - let validator = AliasValidator::new(banned_substrings); - - let bad_alias = "blueName"; - let result = validator.validate(bad_alias); - - assert_eq!( - result, - Err(ValidatorError::Forbidden( - bad_alias.to_owned(), - "blue".to_owned() - )) - ); - } - - #[test] - fn single_case_insensitive_match() { - let banned_substrings = vec!["GrEEn".to_owned()]; - let validator = AliasValidator::new(banned_substrings); - - let bad_alias = "gReenName"; - let result = validator.validate(bad_alias); - - assert_eq!( - result, - Err(ValidatorError::Forbidden( - bad_alias.to_owned(), - "green".to_owned() - )) - ); - } - - #[test] - fn mp_matches() { - let banned_substrings = vec!["orange".to_owned()]; - let validator = AliasValidator::new(banned_substrings); - - let good_alias = "ReasonableName"; - let result = validator.validate(good_alias); - - assert_eq!(result, Ok(())); - } - - #[test] - fn too_long() { - let banned_substrings = vec!["orange".to_owned()]; - let validator = AliasValidator::new(banned_substrings); - - let bad_alias = "Thisnameistoolong Muchtoolong MuchTooLongByFar"; - let result = validator.validate(bad_alias); - - assert_eq!( - result, - Err(ValidatorError::TooLong( - bad_alias.to_owned(), - bad_alias.chars().count() - )) - ); - } -} diff --git a/server/src/automod.rs b/server/src/automod.rs index 822dc7f87d..27780e8a8f 100644 --- a/server/src/automod.rs +++ b/server/src/automod.rs @@ -3,7 +3,10 @@ use authc::Uuid; use censor::Censor; use common::comp::AdminRole; use hashbrown::HashMap; -use std::time::{Duration, Instant}; +use std::{ + sync::Arc, + time::{Duration, Instant}, +}; use tracing::info; pub const MAX_BYTES_CHAT_MSG: usize = 256; @@ -20,12 +23,12 @@ pub enum ActionErr { pub struct AutoMod { settings: ModerationSettings, - censor: Censor, + censor: Arc, players: HashMap, } impl AutoMod { - pub fn new(settings: &ModerationSettings, banned_words: Vec) -> Self { + pub fn new(settings: &ModerationSettings, censor: Arc) -> Self { if settings.automod { info!( "Automod enabled, players{} will be subject to automated spam/content filters", @@ -41,7 +44,7 @@ impl AutoMod { Self { settings: settings.clone(), - censor: Censor::Custom(banned_words.into_iter().collect()), + censor, players: HashMap::default(), } } diff --git a/server/src/lib.rs b/server/src/lib.rs index 74d10c43a7..85c2e3a1ad 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -14,7 +14,6 @@ )] #![cfg_attr(not(feature = "worldgen"), feature(const_panic))] -pub mod alias_validator; pub mod automod; mod character_creator; pub mod chunk_generator; @@ -55,7 +54,6 @@ pub use crate::{ #[cfg(feature = "persistent_world")] use crate::terrain_persistence::TerrainPersistence; use crate::{ - alias_validator::AliasValidator, automod::AutoMod, chunk_generator::ChunkGenerator, client::Client, @@ -70,6 +68,7 @@ use crate::{ state_ext::StateExt, sys::sentinel::{DeletedEntities, TrackedStorages}, }; +use censor::Censor; #[cfg(not(feature = "worldgen"))] use common::grid::Grid; use common::{ @@ -340,17 +339,15 @@ impl Server { state.ecs_mut().register::(); state.ecs_mut().register::(); + // Load banned words list let banned_words = settings.moderation.load_banned_words(data_dir); - - //Alias validator - state - .ecs_mut() - .insert(AliasValidator::new(banned_words.clone())); + let censor = Arc::new(Censor::Custom(banned_words.into_iter().collect())); + state.ecs_mut().insert(Arc::clone(&censor)); // Init automod state .ecs_mut() - .insert(AutoMod::new(&settings.moderation, banned_words)); + .insert(AutoMod::new(&settings.moderation, censor)); #[cfg(feature = "worldgen")] let (world, index) = World::generate( diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 389bbbdf9a..d9a12f58f0 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -1,5 +1,4 @@ use crate::{ - alias_validator::AliasValidator, automod::AutoMod, character_creator, client::Client, @@ -15,7 +14,7 @@ use common::{ use common_ecs::{Job, Origin, Phase, System}; use common_net::msg::{ClientGeneral, ServerGeneral}; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, WriteExpect}; -use std::sync::atomic::Ordering; +use std::sync::{atomic::Ordering, Arc}; use tracing::debug; impl Sys { @@ -30,7 +29,7 @@ impl Sys { admins: &ReadStorage<'_, Admin>, presences: &ReadStorage<'_, Presence>, editable_settings: &ReadExpect<'_, EditableSettings>, - alias_validator: &ReadExpect<'_, AliasValidator>, + censor: &ReadExpect<'_, Arc>, automod: &AutoMod, msg: ClientGeneral, ) -> Result<(), crate::error::Error> { @@ -138,9 +137,12 @@ impl Sys { offhand, body, } => { - if let Err(error) = alias_validator.validate(&alias) { - debug!(?error, ?alias, "denied alias as it contained a banned word"); - client.send(ServerGeneral::CharacterActionError(error.to_string()))?; + if censor.check(&alias) { + debug!(?alias, "denied alias as it contained a banned word"); + client.send(ServerGeneral::CharacterActionError(format!( + "Alias '{}' contains a banned word", + alias + )))?; } else if let Some(player) = players.get(entity) { if let Err(error) = character_creator::create_character( entity, @@ -163,9 +165,12 @@ impl Sys { } }, ClientGeneral::EditCharacter { id, alias, body } => { - if let Err(error) = alias_validator.validate(&alias) { - debug!(?error, ?alias, "denied alias as it contained a banned word"); - client.send(ServerGeneral::CharacterActionError(error.to_string()))?; + if censor.check(&alias) { + debug!(?alias, "denied alias as it contained a banned word"); + client.send(ServerGeneral::CharacterActionError(format!( + "Alias '{}' contains a banned word", + alias + )))?; } else if let Some(player) = players.get(entity) { if let Err(error) = character_creator::edit_character( entity, @@ -220,7 +225,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Admin>, ReadStorage<'a, Presence>, ReadExpect<'a, EditableSettings>, - ReadExpect<'a, AliasValidator>, + ReadExpect<'a, Arc>, ReadExpect<'a, AutoMod>, ); @@ -241,7 +246,7 @@ impl<'a> System<'a> for Sys { admins, presences, editable_settings, - alias_validator, + censor, automod, ): Self::SystemData, ) { @@ -260,7 +265,7 @@ impl<'a> System<'a> for Sys { &admins, &presences, &editable_settings, - &alias_validator, + &censor, &automod, msg, )