feat: forbid certain usernames & room aliases
squashed from https://gitlab.com/famedly/conduit/-/merge_requests/582 Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
parent
784d307425
commit
fc93b29abe
8 changed files with 143 additions and 1 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
@ -402,6 +402,7 @@ dependencies = [
|
||||||
"hyperlocal",
|
"hyperlocal",
|
||||||
"image",
|
"image",
|
||||||
"ipaddress",
|
"ipaddress",
|
||||||
|
"itertools 0.12.1",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lru-cache",
|
"lru-cache",
|
||||||
|
@ -422,6 +423,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_html_form",
|
"serde_html_form",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_regex",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha-1",
|
"sha-1",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
@ -1123,6 +1125,15 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
|
@ -2191,7 +2202,7 @@ name = "ruma-state-res"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=68c9bb0930f2195fa8672fbef9633ef62737df5d#68c9bb0930f2195fa8672fbef9633ef62737df5d"
|
source = "git+https://github.com/ruma/ruma?rev=68c9bb0930f2195fa8672fbef9633ef62737df5d#68c9bb0930f2195fa8672fbef9633ef62737df5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools 0.11.0",
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
"ruma-events",
|
"ruma-events",
|
||||||
|
@ -2404,6 +2415,16 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_regex"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
|
|
@ -62,6 +62,9 @@ ring = "0.17.7"
|
||||||
trust-dns-resolver = "0.23.2"
|
trust-dns-resolver = "0.23.2"
|
||||||
# Used to find matching events for appservices
|
# Used to find matching events for appservices
|
||||||
regex = "1.10.3"
|
regex = "1.10.3"
|
||||||
|
# Used to load forbidden room/user regex from config
|
||||||
|
serde_regex = "1.1.0"
|
||||||
|
itertools = "0.12.1"
|
||||||
# jwt jsonwebtokens
|
# jwt jsonwebtokens
|
||||||
jsonwebtoken = "9.2.0"
|
jsonwebtoken = "9.2.0"
|
||||||
# Performance measurements
|
# Performance measurements
|
||||||
|
|
|
@ -54,6 +54,17 @@ pub async fn get_register_available_route(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.forbidden_usernames()
|
||||||
|
.is_match(user_id.localpart())
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Username is forbidden.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO add check for appservice namespaces
|
// TODO add check for appservice namespaces
|
||||||
|
|
||||||
// If no if check is true we have an username that's available to be used.
|
// If no if check is true we have an username that's available to be used.
|
||||||
|
@ -120,12 +131,25 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
ErrorKind::InvalidUsername,
|
ErrorKind::InvalidUsername,
|
||||||
"Username is invalid.",
|
"Username is invalid.",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
if services().users.exists(&proposed_user_id)? {
|
if services().users.exists(&proposed_user_id)? {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::UserInUse,
|
ErrorKind::UserInUse,
|
||||||
"Desired user ID is already taken.",
|
"Desired user ID is already taken.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.forbidden_usernames()
|
||||||
|
.is_match(proposed_user_id.localpart())
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Username is forbidden.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
proposed_user_id
|
proposed_user_id
|
||||||
}
|
}
|
||||||
_ => loop {
|
_ => loop {
|
||||||
|
|
|
@ -26,6 +26,17 @@ pub async fn create_alias_route(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.forbidden_room_names()
|
||||||
|
.is_match(body.room_alias.alias())
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Room alias is forbidden.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if services()
|
if services()
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
|
|
|
@ -166,6 +166,18 @@ pub async fn create_room_route(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if room alias is forbidden
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.forbidden_room_names()
|
||||||
|
.is_match(localpart)
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Room alias name is forbidden.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let alias = RoomAliasId::parse(format!(
|
let alias = RoomAliasId::parse(format!(
|
||||||
"#{}:{}",
|
"#{}:{}",
|
||||||
localpart,
|
localpart,
|
||||||
|
|
|
@ -7,6 +7,8 @@ use std::{
|
||||||
|
|
||||||
use figment::Figment;
|
use figment::Figment;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use regex::RegexSet;
|
||||||
use ruma::{OwnedServerName, RoomVersionId};
|
use ruma::{OwnedServerName, RoomVersionId};
|
||||||
use serde::{de::IgnoredAny, Deserialize};
|
use serde::{de::IgnoredAny, Deserialize};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
@ -132,6 +134,14 @@ pub struct Config {
|
||||||
#[serde(default = "default_ip_range_denylist")]
|
#[serde(default = "default_ip_range_denylist")]
|
||||||
pub ip_range_denylist: Vec<String>,
|
pub ip_range_denylist: Vec<String>,
|
||||||
|
|
||||||
|
#[serde(default = "RegexSet::empty")]
|
||||||
|
#[serde(with = "serde_regex")]
|
||||||
|
pub forbidden_room_names: RegexSet,
|
||||||
|
|
||||||
|
#[serde(default = "RegexSet::empty")]
|
||||||
|
#[serde(with = "serde_regex")]
|
||||||
|
pub forbidden_usernames: RegexSet,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub catchall: BTreeMap<String, IgnoredAny>,
|
pub catchall: BTreeMap<String, IgnoredAny>,
|
||||||
}
|
}
|
||||||
|
@ -319,6 +329,12 @@ impl fmt::Display for Config {
|
||||||
}
|
}
|
||||||
&lst.join(", ")
|
&lst.join(", ")
|
||||||
}),
|
}),
|
||||||
|
("Forbidden usernames", {
|
||||||
|
&self.forbidden_usernames.patterns().iter().join(", ")
|
||||||
|
}),
|
||||||
|
("Forbidden room names", {
|
||||||
|
&self.forbidden_room_names.patterns().iter().join(", ")
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut msg: String = "Active config values:\n\n".to_owned();
|
let mut msg: String = "Active config values:\n\n".to_owned();
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
use abstraction::{KeyValueDatabaseEngine, KvTree};
|
use abstraction::{KeyValueDatabaseEngine, KvTree};
|
||||||
use argon2::{password_hash::SaltString, PasswordHasher, PasswordVerifier};
|
use argon2::{password_hash::SaltString, PasswordHasher, PasswordVerifier};
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
|
use itertools::Itertools;
|
||||||
use lru_cache::LruCache;
|
use lru_cache::LruCache;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
@ -971,6 +972,51 @@ impl KeyValueDatabase {
|
||||||
latest_database_version
|
latest_database_version
|
||||||
);
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
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(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Loaded {} database with version {}",
|
"Loaded {} database with version {}",
|
||||||
services().globals.config.database_backend,
|
services().globals.config.database_backend,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod data;
|
mod data;
|
||||||
use argon2::Argon2;
|
use argon2::Argon2;
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
|
use regex::RegexSet;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
||||||
OwnedServerSigningKeyId, OwnedUserId,
|
OwnedServerSigningKeyId, OwnedUserId,
|
||||||
|
@ -389,6 +390,14 @@ impl Service<'_> {
|
||||||
&self.config.emergency_password
|
&self.config.emergency_password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn forbidden_room_names(&self) -> &RegexSet {
|
||||||
|
&self.config.forbidden_room_names
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forbidden_usernames(&self) -> &RegexSet {
|
||||||
|
&self.config.forbidden_usernames
|
||||||
|
}
|
||||||
|
|
||||||
pub fn allow_local_presence(&self) -> bool {
|
pub fn allow_local_presence(&self) -> bool {
|
||||||
self.config.allow_local_presence
|
self.config.allow_local_presence
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue