feat: forbid certain usernames & room aliases #1007

Open
Kladky wants to merge 5 commits from feature/forbidden-usernames-rooms into next
8 changed files with 96 additions and 67 deletions
Showing only changes of commit 12f6f147eb - Show all commits

12
Cargo.lock generated
View file

@ -387,6 +387,7 @@ dependencies = [
"http",
"hyper",
"image",
"itertools 0.12.0",
"jsonwebtoken",
"lazy_static",
"lru-cache",
@ -1157,6 +1158,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -2195,7 +2205,7 @@ name = "ruma-state-res"
version = "0.9.1"
source = "git+https://github.com/ruma/ruma?rev=b4853aa8fa5e3a24e3689fc88044de9915f6ab67#b4853aa8fa5e3a24e3689fc88044de9915f6ab67"
dependencies = [
"itertools",
"itertools 0.11.0",
"js_int",
"ruma-common",
"ruma-events",

View file

@ -80,6 +80,7 @@ trust-dns-resolver = "0.22.0"
regex = "1.8.1"
# Used to load forbidden room/user regex from config
serde_regex = "1.1.0"
itertools = "0.12.0"
# jwt jsonwebtokens
jsonwebtoken = "9.2.0"
# Performance measurements

View file

@ -32,13 +32,15 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
pub async fn get_register_available_route(
body: Ruma<get_username_availability::v3::Request>,
) -> Result<get_username_availability::v3::Response> {
for pattern in services().globals.forbidden_usernames() {
if pattern.is_match(&body.username.to_lowercase()) {
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Username is forbidden.",
));
}
if services()
.globals
.forbidden_usernames()
.is_match(&body.username.to_lowercase())
{
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Username is forbidden.",
));
}
// Validate user id
let user_id = UserId::parse_with_server_name(
@ -96,13 +98,15 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
let user_id = match (&body.username, is_guest) {
(Some(username), false) => {
for pattern in services().globals.forbidden_usernames() {
if pattern.is_match(&username.to_lowercase()) {
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Username is forbidden.",
));
}
if services()
.globals
.forbidden_usernames()
.is_match(&username.to_lowercase())
{
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Username is forbidden.",
));
}
let proposed_user_id = UserId::parse_with_server_name(
username.to_lowercase(),

View file

@ -26,13 +26,15 @@ pub async fn create_alias_route(
));
}
for pattern in services().globals.forbidden_room_names() {
if pattern.is_match(body.room_alias.alias()) {
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Room alias is forbidden.",
));
}
if services()
.globals
.forbidden_room_names()
.is_match(body.room_alias.alias())
{
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Room alias is forbidden.",
));
}
if services()

View file

@ -81,13 +81,15 @@ pub async fn create_room_route(
body.room_alias_name
.as_ref()
.map_or(Ok(None), |localpart| {
for pattern in services().globals.forbidden_room_names() {
if pattern.is_match(localpart) {
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Room alias is forbidden.",
));
}
if services()
.globals
.forbidden_room_names()
.is_match(localpart)
{
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Room alias is forbidden.",
));
}
// TODO: Check for invalid characters and maximum length
let alias = RoomAliasId::parse(format!(

View file

@ -1,4 +1,5 @@
use regex::Regex;
use itertools::Itertools;
use regex::RegexSet;
use std::{
collections::BTreeMap,
@ -87,13 +88,13 @@ pub struct Config {
#[serde(flatten)]
pub catchall: BTreeMap<String, IgnoredAny>,
#[serde(default = "Vec::new")]
#[serde(default = "RegexSet::empty")]
#[serde(with = "serde_regex")]
pub forbidden_room_names: Vec<Regex>,
pub forbidden_room_names: RegexSet,
#[serde(default = "Vec::new")]
#[serde(default = "RegexSet::empty")]
#[serde(with = "serde_regex")]
pub forbidden_usernames: Vec<Regex>,
pub forbidden_usernames: RegexSet,
}
#[derive(Clone, Debug, Deserialize)]
@ -206,18 +207,10 @@ impl fmt::Display for Config {
&lst.join(", ")
}),
("Forbidden usernames", {
let mut lst = vec![];
for pattern in &self.forbidden_usernames {
lst.push(pattern.to_string());
}
&lst.join(", ")
&self.forbidden_usernames.patterns().iter().join(", ")
}),
("Forbidden room names", {
let mut lst = vec![];
for pattern in &self.forbidden_room_names {
lst.push(pattern.to_string());
}
&lst.join(", ")
&self.forbidden_room_names.patterns().iter().join(", ")
}),
];

View file

@ -7,7 +7,9 @@ use crate::{
};
use abstraction::{KeyValueDatabaseEngine, KvTree};
use directories::ProjectDirs;
use itertools::Itertools;
use lru_cache::LruCache;
use ruma::{
events::{
push_rules::{PushRulesEvent, PushRulesEventContent},
@ -947,31 +949,46 @@ impl KeyValueDatabase {
latest_database_version
);
for user in services().users.iter() {
let user_id = user?;
for pattern in &services().globals.config.forbidden_usernames {
if pattern.is_match(user_id.localpart()) {
warn!(
"User {} matches the following forbidden username pattern: {}",
user_id.to_string(),
pattern
)
{
let patterns = &services().globals.config.forbidden_usernames;
if !patterns.is_empty() {
for user in services().users.iter() {
let user_id = user?;
let matches = patterns.matches(user_id.localpart());
if matches.matched_any() {
warn!(
"User {} matches the following forbidden username patterns: {}",
user_id.to_string(),
matches
.into_iter()
.map(|x| &patterns.patterns()[x])
.join(", ")
)
}
}
}
}
for address in services().rooms.metadata.iter_ids() {
let room_id = address?;
let room_aliases = services().rooms.alias.local_aliases_for_room(&room_id);
for room_alias_result in room_aliases {
let room_alias = room_alias_result?;
for pattern in &services().globals.config.forbidden_room_names {
if pattern.is_match(room_alias.alias()) {
warn!(
"Room with alias {} ({}) matches the following forbidden room name pattern: {}",
room_alias, &room_id,
pattern
{
let patterns = &services().globals.config.forbidden_room_names;
if !patterns.is_empty() {
for address in services().rooms.metadata.iter_ids() {
let room_id = address?;
let room_aliases = services().rooms.alias.local_aliases_for_room(&room_id);
for room_alias_result in room_aliases {
let room_alias = room_alias_result?;
let matches = patterns.matches(room_alias.alias());
if matches.matched_any() {
warn!(
"Room with alias {} ({}) matches the following forbidden room name patterns: {}",
room_alias,
&room_id,
matches
.into_iter()
.map(|x| &patterns.patterns()[x])
.join(", ")
)
}
}
}
}

View file

@ -1,6 +1,6 @@
mod data;
pub use data::Data;
use regex::Regex;
use regex::RegexSet;
use ruma::{
serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName,
OwnedServerSigningKeyId, OwnedUserId,
@ -353,11 +353,11 @@ impl Service {
&self.config.emergency_password
}
pub fn forbidden_room_names(&self) -> &Vec<Regex> {
pub fn forbidden_room_names(&self) -> &RegexSet {
&self.config.forbidden_room_names
}
pub fn forbidden_usernames(&self) -> &Vec<Regex> {
pub fn forbidden_usernames(&self) -> &RegexSet {
&self.config.forbidden_usernames
}