Validate acct during profile creation
This commit is contained in:
parent
429f530a71
commit
8b6aef2b7a
5 changed files with 67 additions and 10 deletions
|
@ -1,3 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
|
@ -178,12 +181,6 @@ pub struct ActorAddress {
|
|||
pub instance: String,
|
||||
}
|
||||
|
||||
impl ToString for ActorAddress {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}@{}", self.username, self.instance)
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorAddress {
|
||||
pub fn is_local(&self, instance_host: &str) -> bool {
|
||||
self.instance == instance_host
|
||||
|
@ -199,6 +196,30 @@ impl ActorAddress {
|
|||
}
|
||||
}
|
||||
|
||||
// See also: USERNAME_RE in models::profiles::validators
|
||||
const ACTOR_ADDRESS_RE: &str = r"(?P<username>[\w\.-]+)@(?P<instance>[\w\.-]+)";
|
||||
|
||||
impl FromStr for ActorAddress {
|
||||
type Err = ValidationError;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
let actor_address_re = Regex::new(ACTOR_ADDRESS_RE).unwrap();
|
||||
let caps = actor_address_re.captures(value)
|
||||
.ok_or(ValidationError("invalid actor address"))?;
|
||||
let actor_address = Self {
|
||||
username: caps["username"].to_string(),
|
||||
instance: caps["instance"].to_string(),
|
||||
};
|
||||
Ok(actor_address)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ActorAddress {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}@{}", self.username, self.instance)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ActorKeyError = rsa::pkcs8::Error;
|
||||
|
||||
pub fn get_local_actor(
|
||||
|
@ -330,6 +351,15 @@ mod tests {
|
|||
const INSTANCE_HOST: &str = "example.com";
|
||||
const INSTANCE_URL: &str = "https://example.com";
|
||||
|
||||
#[test]
|
||||
fn test_actor_address_parsing() {
|
||||
let value = "user_1@example.com";
|
||||
let actor_address = value.parse::<ActorAddress>().unwrap();
|
||||
assert_eq!(actor_address.username, "user_1");
|
||||
assert_eq!(actor_address.instance, "example.com");
|
||||
assert_eq!(actor_address.to_string(), value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_actor_address() {
|
||||
let actor = Actor {
|
||||
|
|
|
@ -40,7 +40,7 @@ enum SearchQuery {
|
|||
fn parse_profile_query(query: &str) ->
|
||||
Result<(String, Option<String>), ValidationError>
|
||||
{
|
||||
// See also: USERNAME_RE in models::profiles::validators
|
||||
// See also: ACTOR_ADDRESS_RE in activitypub::actors::types
|
||||
let acct_query_re =
|
||||
Regex::new(r"^(@|!)?(?P<user>[\w\.-]+)(@(?P<instance>[\w\.-]+))?$").unwrap();
|
||||
let acct_query_caps = acct_query_re.captures(query)
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::errors::{DatabaseError, ValidationError};
|
|||
use crate::models::profiles::queries::get_profiles_by_accts;
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
|
||||
// See also: USERNAME_RE in models::profiles::validators
|
||||
// See also: ACTOR_ADDRESS_RE in activitypub::actors::types
|
||||
const MENTION_RE: &str = r"@?(?P<user>[\w\.-]+)@(?P<instance>.+)";
|
||||
const MENTION_SEARCH_RE: &str = r"(?m)(?P<before>^|\s|[\(])@(?P<user>[\w\.-]+)@(?P<instance>\S+)";
|
||||
const MENTION_SEARCH_SECONDARY_RE: &str = r"^(?P<instance>[\w\.-]+\w)(?P<after>[\.,:?\)]?(<br>)?)$";
|
||||
|
|
|
@ -10,7 +10,7 @@ use serde::{
|
|||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::activitypub::actors::types::Actor;
|
||||
use crate::activitypub::actors::types::{Actor, ActorAddress};
|
||||
use crate::activitypub::identifiers::local_actor_id;
|
||||
use crate::database::json_macro::{json_from_sql, json_to_sql};
|
||||
use crate::errors::{ConversionError, ValidationError};
|
||||
|
@ -337,6 +337,17 @@ impl ProfileCreateData {
|
|||
if let Some(display_name) = &self.display_name {
|
||||
validate_display_name(display_name)?;
|
||||
};
|
||||
let acct_username = if self.actor_json.is_none() {
|
||||
// Local profile
|
||||
self.acct.clone()
|
||||
} else {
|
||||
// Remote profile
|
||||
let ActorAddress { username, .. } = self.acct.parse::<ActorAddress>()?;
|
||||
username
|
||||
};
|
||||
if self.username != acct_username {
|
||||
return Err(ValidationError("username doesn't match acct"));
|
||||
};
|
||||
if let Some(bio) = &self.bio {
|
||||
let cleaned_bio = clean_bio(bio, self.actor_json.is_some())?;
|
||||
self.bio = Some(cleaned_bio);
|
||||
|
@ -477,4 +488,20 @@ mod tests {
|
|||
remote_profile.acct,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clean_profile_create_data() {
|
||||
let mut profile_data = ProfileCreateData {
|
||||
username: "test".to_string(),
|
||||
display_name: Some("Test Test".to_string()),
|
||||
acct: "test@example.org".to_string(),
|
||||
actor_json: Some(Actor {
|
||||
id: "https://example.org/test".to_string(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let result = profile_data.clean();
|
||||
assert_eq!(result.is_ok(), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ async fn get_user_info(
|
|||
) -> Result<JsonResourceDescriptor, HttpError> {
|
||||
// Parse 'acct' URI
|
||||
// https://datatracker.ietf.org/doc/html/rfc7565#section-7
|
||||
// See also: USERNAME_RE in models::profiles::validators
|
||||
// See also: ACTOR_ADDRESS_RE in activitypub::actors::types
|
||||
let uri_regexp = Regex::new(r"acct:(?P<user>[\w\.-]+)@(?P<instance>.+)").unwrap();
|
||||
let uri_caps = uri_regexp.captures(&query_params.resource)
|
||||
.ok_or(ValidationError("invalid query target"))?;
|
||||
|
|
Loading…
Reference in a new issue