mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Simplified and improved alias validation
This commit is contained in:
parent
4d110a542c
commit
4e103c433f
@ -1,139 +0,0 @@
|
||||
use common::character::MAX_NAME_LENGTH;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AliasValidator {
|
||||
banned_substrings: Vec<String>,
|
||||
}
|
||||
|
||||
impl AliasValidator {
|
||||
pub fn new(banned_substrings: Vec<String>) -> 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()
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
@ -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<Censor>,
|
||||
players: HashMap<Uuid, PlayerState>,
|
||||
}
|
||||
|
||||
impl AutoMod {
|
||||
pub fn new(settings: &ModerationSettings, banned_words: Vec<String>) -> Self {
|
||||
pub fn new(settings: &ModerationSettings, censor: Arc<Censor>) -> 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(),
|
||||
}
|
||||
}
|
||||
|
@ -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::<login_provider::PendingLogin>();
|
||||
state.ecs_mut().register::<RepositionOnChunkLoad>();
|
||||
|
||||
// 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(
|
||||
|
@ -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<censor::Censor>>,
|
||||
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<censor::Censor>>,
|
||||
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,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user