Actor name length config dess (#1672)
* Make length of user/community name configurable (fixes #1306) * use single config value, fix migrations, increase displayname length * remove debug statements * add helper method for setting actor_name_max_length * move function * Fixing some defaults Co-authored-by: Felix Ableitner <me@nutomic.com>
This commit is contained in:
parent
fcae2189eb
commit
6ffa291ec9
11 changed files with 74 additions and 41 deletions
|
@ -39,6 +39,8 @@
|
||||||
pictrs_url: "http://pictrs:8080"
|
pictrs_url: "http://pictrs:8080"
|
||||||
# address where iframely is available
|
# address where iframely is available
|
||||||
iframely_url: "http://iframely"
|
iframely_url: "http://iframely"
|
||||||
|
# maximum length of local community and user names
|
||||||
|
actor_name_max_length: 20
|
||||||
# rate limits for various user actions, by user ip
|
# rate limits for various user actions, by user ip
|
||||||
rate_limit: {
|
rate_limit: {
|
||||||
# maximum number of messages created in interval
|
# maximum number of messages created in interval
|
||||||
|
|
|
@ -28,7 +28,7 @@ use lemmy_db_schema::source::{
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
use lemmy_db_views_actor::community_view::CommunityView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
apub::generate_actor_keypair,
|
apub::generate_actor_keypair,
|
||||||
utils::{check_slurs, check_slurs_opt, is_valid_community_name},
|
utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
|
||||||
ApiError,
|
ApiError,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
LemmyError,
|
LemmyError,
|
||||||
|
@ -56,7 +56,7 @@ impl PerformCrud for CreateCommunity {
|
||||||
check_slurs(&data.title)?;
|
check_slurs(&data.title)?;
|
||||||
check_slurs_opt(&data.description)?;
|
check_slurs_opt(&data.description)?;
|
||||||
|
|
||||||
if !is_valid_community_name(&data.name) {
|
if !is_valid_actor_name(&data.name) {
|
||||||
return Err(ApiError::err("invalid_community_name").into());
|
return Err(ApiError::err("invalid_community_name").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ use lemmy_utils::{
|
||||||
apub::generate_actor_keypair,
|
apub::generate_actor_keypair,
|
||||||
claims::Claims,
|
claims::Claims,
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
utils::{check_slurs, is_valid_username},
|
utils::{check_slurs, is_valid_actor_name},
|
||||||
ApiError,
|
ApiError,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
LemmyError,
|
LemmyError,
|
||||||
|
@ -91,7 +91,7 @@ impl PerformCrud for Register {
|
||||||
check_slurs(&data.username)?;
|
check_slurs(&data.username)?;
|
||||||
|
|
||||||
let actor_keypair = generate_actor_keypair()?;
|
let actor_keypair = generate_actor_keypair()?;
|
||||||
if !is_valid_username(&data.username) {
|
if !is_valid_actor_name(&data.username) {
|
||||||
return Err(ApiError::err("invalid_username").into());
|
return Err(ApiError::err("invalid_username").into());
|
||||||
}
|
}
|
||||||
let actor_id = generate_apub_endpoint(EndpointType::Person, &data.username)?;
|
let actor_id = generate_apub_endpoint(EndpointType::Person, &data.username)?;
|
||||||
|
|
|
@ -89,12 +89,12 @@ impl actix_web::error::ResponseError for LemmyError {
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
|
pub static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
|
||||||
"^group:([a-z0-9_]{{3, 20}})@{}$",
|
"^group:([a-z0-9_]{{3,}})@{}$",
|
||||||
Settings::get().hostname()
|
Settings::get().hostname()
|
||||||
))
|
))
|
||||||
.expect("compile webfinger regex");
|
.expect("compile webfinger regex");
|
||||||
pub static ref WEBFINGER_USERNAME_REGEX: Regex = Regex::new(&format!(
|
pub static ref WEBFINGER_USERNAME_REGEX: Regex = Regex::new(&format!(
|
||||||
"^acct:([a-z0-9_]{{3, 20}})@{}$",
|
"^acct:([a-z0-9_]{{3,}})@{}$",
|
||||||
Settings::get().hostname()
|
Settings::get().hostname()
|
||||||
))
|
))
|
||||||
.expect("compile webfinger regex");
|
.expect("compile webfinger regex");
|
||||||
|
|
|
@ -18,6 +18,7 @@ impl Default for Settings {
|
||||||
pictrs_url: Some("http://pictrs:8080".into()),
|
pictrs_url: Some("http://pictrs:8080".into()),
|
||||||
iframely_url: Some("http://iframely".into()),
|
iframely_url: Some("http://iframely".into()),
|
||||||
additional_slurs: None,
|
additional_slurs: None,
|
||||||
|
actor_name_max_length: Some(20),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,29 +132,52 @@ impl Settings {
|
||||||
Ok(Self::read_config_file()?)
|
Ok(Self::read_config_file()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn database(&self) -> DatabaseConfig {
|
|
||||||
self.database.to_owned().unwrap_or_default()
|
|
||||||
}
|
|
||||||
pub fn hostname(&self) -> String {
|
pub fn hostname(&self) -> String {
|
||||||
self.hostname.to_owned().unwrap_or_default()
|
self.hostname.to_owned().expect("No hostname given")
|
||||||
}
|
}
|
||||||
pub fn bind(&self) -> IpAddr {
|
pub fn bind(&self) -> IpAddr {
|
||||||
self.bind.expect("return bind address")
|
self.bind.expect("return bind address")
|
||||||
}
|
}
|
||||||
pub fn port(&self) -> u16 {
|
pub fn port(&self) -> u16 {
|
||||||
self.port.unwrap_or_default()
|
self
|
||||||
|
.port
|
||||||
|
.unwrap_or_else(|| Settings::default().port.expect("missing port"))
|
||||||
}
|
}
|
||||||
pub fn tls_enabled(&self) -> bool {
|
pub fn tls_enabled(&self) -> bool {
|
||||||
self.tls_enabled.unwrap_or_default()
|
self.tls_enabled.unwrap_or_else(|| {
|
||||||
|
Settings::default()
|
||||||
|
.tls_enabled
|
||||||
|
.expect("missing tls_enabled")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub fn jwt_secret(&self) -> String {
|
pub fn jwt_secret(&self) -> String {
|
||||||
self.jwt_secret.to_owned().unwrap_or_default()
|
self
|
||||||
|
.jwt_secret
|
||||||
|
.to_owned()
|
||||||
|
.unwrap_or_else(|| Settings::default().jwt_secret.expect("missing jwt_secret"))
|
||||||
}
|
}
|
||||||
pub fn pictrs_url(&self) -> String {
|
pub fn pictrs_url(&self) -> String {
|
||||||
self.pictrs_url.to_owned().unwrap_or_default()
|
self
|
||||||
|
.pictrs_url
|
||||||
|
.to_owned()
|
||||||
|
.unwrap_or_else(|| Settings::default().pictrs_url.expect("missing pictrs_url"))
|
||||||
}
|
}
|
||||||
pub fn iframely_url(&self) -> String {
|
pub fn iframely_url(&self) -> String {
|
||||||
self.iframely_url.to_owned().unwrap_or_default()
|
self.iframely_url.to_owned().unwrap_or_else(|| {
|
||||||
|
Settings::default()
|
||||||
|
.iframely_url
|
||||||
|
.expect("missing iframely_url")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn actor_name_max_length(&self) -> usize {
|
||||||
|
self.actor_name_max_length.unwrap_or_else(|| {
|
||||||
|
Settings::default()
|
||||||
|
.actor_name_max_length
|
||||||
|
.expect("missing actor name length")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn database(&self) -> DatabaseConfig {
|
||||||
|
self.database.to_owned().unwrap_or_default()
|
||||||
}
|
}
|
||||||
pub fn rate_limit(&self) -> RateLimitConfig {
|
pub fn rate_limit(&self) -> RateLimitConfig {
|
||||||
self.rate_limit.to_owned().unwrap_or_default()
|
self.rate_limit.to_owned().unwrap_or_default()
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub struct Settings {
|
||||||
pub(crate) email: Option<EmailConfig>,
|
pub(crate) email: Option<EmailConfig>,
|
||||||
pub(crate) setup: Option<SetupConfig>,
|
pub(crate) setup: Option<SetupConfig>,
|
||||||
pub(crate) additional_slurs: Option<String>,
|
pub(crate) additional_slurs: Option<String>,
|
||||||
|
pub(crate) actor_name_max_length: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
is_valid_community_name,
|
is_valid_actor_name,
|
||||||
is_valid_display_name,
|
is_valid_display_name,
|
||||||
is_valid_matrix_id,
|
is_valid_matrix_id,
|
||||||
is_valid_post_title,
|
is_valid_post_title,
|
||||||
is_valid_username,
|
|
||||||
remove_slurs,
|
remove_slurs,
|
||||||
scrape_text_for_mentions,
|
scrape_text_for_mentions,
|
||||||
slur_check,
|
slur_check,
|
||||||
|
@ -21,12 +20,12 @@ fn test_mentions_regex() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_register_username() {
|
fn test_valid_actor_name() {
|
||||||
assert!(is_valid_username("Hello_98"));
|
assert!(is_valid_actor_name("Hello_98"));
|
||||||
assert!(is_valid_username("ten"));
|
assert!(is_valid_actor_name("ten"));
|
||||||
assert!(!is_valid_username("Hello-98"));
|
assert!(!is_valid_actor_name("Hello-98"));
|
||||||
assert!(!is_valid_username("a"));
|
assert!(!is_valid_actor_name("a"));
|
||||||
assert!(!is_valid_username(""));
|
assert!(!is_valid_actor_name(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -41,15 +40,6 @@ fn test_valid_display_name() {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_valid_community_name() {
|
|
||||||
assert!(is_valid_community_name("example"));
|
|
||||||
assert!(is_valid_community_name("example_community"));
|
|
||||||
assert!(!is_valid_community_name("Example"));
|
|
||||||
assert!(!is_valid_community_name("Ex"));
|
|
||||||
assert!(!is_valid_community_name(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_post_title() {
|
fn test_valid_post_title() {
|
||||||
assert!(is_valid_post_title("Post Title"));
|
assert!(is_valid_post_title("Post Title"));
|
||||||
|
|
|
@ -22,8 +22,7 @@ lazy_static! {
|
||||||
// TODO keep this old one, it didn't work with port well tho
|
// TODO keep this old one, it didn't work with port well tho
|
||||||
// static ref MENTIONS_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)").expect("compile regex");
|
// static ref MENTIONS_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)").expect("compile regex");
|
||||||
static ref MENTIONS_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").expect("compile regex");
|
static ref MENTIONS_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").expect("compile regex");
|
||||||
static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").expect("compile regex");
|
static ref VALID_ACTOR_NAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex");
|
||||||
static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").expect("compile regex");
|
|
||||||
static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").expect("compile regex");
|
static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").expect("compile regex");
|
||||||
static ref VALID_MATRIX_ID_REGEX: Regex = Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex");
|
static ref VALID_MATRIX_ID_REGEX: Regex = Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex");
|
||||||
// taken from https://en.wikipedia.org/wiki/UTM_parameters
|
// taken from https://en.wikipedia.org/wiki/UTM_parameters
|
||||||
|
@ -116,8 +115,9 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
|
||||||
out.into_iter().unique().collect()
|
out.into_iter().unique().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_username(name: &str) -> bool {
|
pub fn is_valid_actor_name(name: &str) -> bool {
|
||||||
VALID_USERNAME_REGEX.is_match(name)
|
name.chars().count() <= Settings::get().actor_name_max_length()
|
||||||
|
&& VALID_ACTOR_NAME_REGEX.is_match(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't do a regex here, reverse lookarounds not supported
|
// Can't do a regex here, reverse lookarounds not supported
|
||||||
|
@ -125,17 +125,13 @@ pub fn is_valid_display_name(name: &str) -> bool {
|
||||||
!name.starts_with('@')
|
!name.starts_with('@')
|
||||||
&& !name.starts_with('\u{200b}')
|
&& !name.starts_with('\u{200b}')
|
||||||
&& name.chars().count() >= 3
|
&& name.chars().count() >= 3
|
||||||
&& name.chars().count() <= 20
|
&& name.chars().count() <= Settings::get().actor_name_max_length()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
|
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
|
||||||
VALID_MATRIX_ID_REGEX.is_match(matrix_id)
|
VALID_MATRIX_ID_REGEX.is_match(matrix_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_community_name(name: &str) -> bool {
|
|
||||||
VALID_COMMUNITY_NAME_REGEX.is_match(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid_post_title(title: &str) -> bool {
|
pub fn is_valid_post_title(title: &str) -> bool {
|
||||||
VALID_POST_TITLE_REGEX.is_match(title)
|
VALID_POST_TITLE_REGEX.is_match(title)
|
||||||
}
|
}
|
||||||
|
|
10
migrations/2021-07-20-102033_actor_name_length/down.sql
Normal file
10
migrations/2021-07-20-102033_actor_name_length/down.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
DROP VIEW person_alias_1;
|
||||||
|
DROP VIEW person_alias_2;
|
||||||
|
|
||||||
|
ALTER TABLE community ALTER COLUMN name TYPE varchar(20);
|
||||||
|
ALTER TABLE community ALTER COLUMN title TYPE varchar(100);
|
||||||
|
ALTER TABLE person ALTER COLUMN name TYPE varchar(20);
|
||||||
|
ALTER TABLE person ALTER COLUMN display_name TYPE varchar(20);
|
||||||
|
|
||||||
|
create view person_alias_1 as select * from person;
|
||||||
|
create view person_alias_2 as select * from person;
|
10
migrations/2021-07-20-102033_actor_name_length/up.sql
Normal file
10
migrations/2021-07-20-102033_actor_name_length/up.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
DROP VIEW person_alias_1;
|
||||||
|
DROP VIEW person_alias_2;
|
||||||
|
|
||||||
|
ALTER TABLE community ALTER COLUMN name TYPE varchar(255);
|
||||||
|
ALTER TABLE community ALTER COLUMN title TYPE varchar(255);
|
||||||
|
ALTER TABLE person ALTER COLUMN name TYPE varchar(255);
|
||||||
|
ALTER TABLE person ALTER COLUMN display_name TYPE varchar(255);
|
||||||
|
|
||||||
|
create view person_alias_1 as select * from person;
|
||||||
|
create view person_alias_2 as select * from person;
|
Loading…
Reference in a new issue