Move ActorAddress type to webfinger::types module

This commit is contained in:
silverpill 2023-01-10 21:26:42 +00:00
parent c953d66c95
commit af332283ed
9 changed files with 74 additions and 68 deletions

View file

@ -1,7 +1,3 @@
use std::fmt;
use std::str::FromStr;
use regex::Regex;
use serde::{ use serde::{
Deserialize, Deserialize,
Deserializer, Deserializer,
@ -26,6 +22,7 @@ use crate::models::users::types::User;
use crate::utils::crypto_rsa::{deserialize_private_key, get_public_key_pem}; use crate::utils::crypto_rsa::{deserialize_private_key, get_public_key_pem};
use crate::utils::files::get_file_url; use crate::utils::files::get_file_url;
use crate::utils::urls::get_hostname; use crate::utils::urls::get_hostname;
use crate::webfinger::types::ActorAddress;
use super::attachments::{ use super::attachments::{
attach_extra_field, attach_extra_field,
attach_identity_proof, attach_identity_proof,
@ -217,46 +214,6 @@ impl Actor {
} }
} }
// See also: USERNAME_RE in models::profiles::validators
const ACTOR_ADDRESS_RE: &str = r"(?P<username>[\w\.-]+)@(?P<hostname>[\w\.-]+)";
pub struct ActorAddress {
pub username: String,
pub hostname: String,
}
impl ActorAddress {
/// Returns acct string, as used in Mastodon
pub fn acct(&self, local_hostname: &str) -> String {
if self.hostname == local_hostname {
self.username.clone()
} else {
self.to_string()
}
}
}
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(),
hostname: caps["hostname"].to_string(),
};
Ok(actor_address)
}
}
impl fmt::Display for ActorAddress {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}@{}", self.username, self.hostname)
}
}
pub type ActorKeyError = rsa::pkcs8::Error; pub type ActorKeyError = rsa::pkcs8::Error;
pub fn get_local_actor( pub fn get_local_actor(
@ -394,15 +351,6 @@ mod tests {
const INSTANCE_HOSTNAME: &str = "example.com"; const INSTANCE_HOSTNAME: &str = "example.com";
const INSTANCE_URL: &str = "https://example.com"; const INSTANCE_URL: &str = "https://example.com";
#[test]
fn test_actor_address_parse_address() {
let value = "user_1@example.com";
let actor_address: ActorAddress = value.parse().unwrap();
assert_eq!(actor_address.username, "user_1");
assert_eq!(actor_address.hostname, "example.com");
assert_eq!(actor_address.to_string(), value);
}
#[test] #[test]
fn test_get_actor_address() { fn test_get_actor_address() {
let actor = Actor { let actor = Actor {

View file

@ -5,7 +5,7 @@ use reqwest::{Client, Method, Proxy, RequestBuilder};
use serde_json::Value; use serde_json::Value;
use crate::activitypub::activity::Object; use crate::activitypub::activity::Object;
use crate::activitypub::actors::types::{Actor, ActorAddress}; use crate::activitypub::actors::types::Actor;
use crate::activitypub::constants::AP_MEDIA_TYPE; use crate::activitypub::constants::AP_MEDIA_TYPE;
use crate::config::Instance; use crate::config::Instance;
use crate::http_signatures::create::{ use crate::http_signatures::create::{
@ -14,7 +14,7 @@ use crate::http_signatures::create::{
}; };
use crate::utils::files::{save_file, sniff_media_type}; use crate::utils::files::{save_file, sniff_media_type};
use crate::utils::urls::guess_protocol; use crate::utils::urls::guess_protocol;
use crate::webfinger::types::JsonResourceDescriptor; use crate::webfinger::types::{ActorAddress, JsonResourceDescriptor};
const FETCHER_CONNECTION_TIMEOUT: u64 = 30; const FETCHER_CONNECTION_TIMEOUT: u64 = 30;

View file

@ -5,10 +5,7 @@ use tokio_postgres::GenericClient;
use crate::activitypub::{ use crate::activitypub::{
activity::Object, activity::Object,
actors::{ actors::helpers::{create_remote_profile, update_remote_profile},
helpers::{create_remote_profile, update_remote_profile},
types::ActorAddress,
},
handlers::create::handle_note, handlers::create::handle_note,
identifiers::parse_local_object_id, identifiers::parse_local_object_id,
receiver::HandlerError, receiver::HandlerError,
@ -24,6 +21,7 @@ use crate::models::profiles::queries::{
get_profile_by_remote_actor_id, get_profile_by_remote_actor_id,
}; };
use crate::models::profiles::types::DbActorProfile; use crate::models::profiles::types::DbActorProfile;
use crate::webfinger::types::ActorAddress;
use super::fetchers::{ use super::fetchers::{
fetch_actor, fetch_actor,
fetch_object, fetch_object,

View file

@ -5,7 +5,6 @@ use tokio_postgres::GenericClient;
use url::Url; use url::Url;
use crate::activitypub::{ use crate::activitypub::{
actors::types::ActorAddress,
fetcher::helpers::{ fetcher::helpers::{
get_or_import_profile_by_actor_id, get_or_import_profile_by_actor_id,
import_post, import_post,
@ -36,6 +35,7 @@ use crate::models::users::{
types::User, types::User,
}; };
use crate::utils::currencies::{validate_wallet_address, Currency}; use crate::utils::currencies::{validate_wallet_address, Currency};
use crate::webfinger::types::ActorAddress;
use super::types::SearchResults; use super::types::SearchResults;
enum SearchQuery { enum SearchQuery {
@ -50,7 +50,7 @@ enum SearchQuery {
fn parse_profile_query(query: &str) -> fn parse_profile_query(query: &str) ->
Result<(String, Option<String>), ValidationError> Result<(String, Option<String>), ValidationError>
{ {
// See also: ACTOR_ADDRESS_RE in activitypub::actors::types // See also: ACTOR_ADDRESS_RE in webfinger::types
let acct_query_re = let acct_query_re =
Regex::new(r"^(@|!)?(?P<username>[\w\.-]+)(@(?P<hostname>[\w\.-]+))?$").unwrap(); Regex::new(r"^(@|!)?(?P<username>[\w\.-]+)(@(?P<hostname>[\w\.-]+))?$").unwrap();
let acct_query_caps = acct_query_re.captures(query) let acct_query_caps = acct_query_re.captures(query)

View file

@ -4,7 +4,6 @@ use actix_web::{get, post, web, HttpResponse, Scope};
use actix_web_httpauth::extractors::bearer::BearerAuth; use actix_web_httpauth::extractors::bearer::BearerAuth;
use crate::activitypub::{ use crate::activitypub::{
actors::types::ActorAddress,
builders::{ builders::{
move_person::prepare_move_person, move_person::prepare_move_person,
undo_follow::prepare_undo_follow, undo_follow::prepare_undo_follow,
@ -24,6 +23,7 @@ use crate::models::{
users::queries::set_user_password, users::queries::set_user_password,
}; };
use crate::utils::passwords::hash_password; use crate::utils::passwords::hash_password;
use crate::webfinger::types::ActorAddress;
use super::helpers::{export_followers, export_follows}; use super::helpers::{export_followers, export_follows};
use super::types::{MoveFollowersRequest, PasswordChangeRequest}; use super::types::{MoveFollowersRequest, PasswordChangeRequest};

View file

@ -3,14 +3,14 @@ use std::collections::HashMap;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use tokio_postgres::GenericClient; use tokio_postgres::GenericClient;
use crate::activitypub::actors::types::ActorAddress;
use crate::database::DatabaseError; use crate::database::DatabaseError;
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::models::profiles::queries::get_profiles_by_accts; use crate::models::profiles::queries::get_profiles_by_accts;
use crate::models::profiles::types::DbActorProfile; use crate::models::profiles::types::DbActorProfile;
use crate::webfinger::types::ActorAddress;
use super::links::is_inside_code_block; use super::links::is_inside_code_block;
// See also: ACTOR_ADDRESS_RE in activitypub::actors::types // See also: ACTOR_ADDRESS_RE in webfinger::types
const MENTION_SEARCH_RE: &str = r"(?m)(?P<before>^|\s|>|[\(])@(?P<mention>[^\s<]+)"; const MENTION_SEARCH_RE: &str = r"(?m)(?P<before>^|\s|>|[\(])@(?P<mention>[^\s<]+)";
const MENTION_SEARCH_SECONDARY_RE: &str = r"^(?P<username>[\w\.-]+)(@(?P<hostname>[\w\.-]+\w))?(?P<after>[\.,:?\)]?)$"; const MENTION_SEARCH_SECONDARY_RE: &str = r"^(?P<username>[\w\.-]+)(@(?P<hostname>[\w\.-]+\w))?(?P<after>[\.,:?\)]?)$";

View file

@ -11,7 +11,7 @@ use serde::{
}; };
use uuid::Uuid; use uuid::Uuid;
use crate::activitypub::actors::types::{Actor, ActorAddress}; use crate::activitypub::actors::types::Actor;
use crate::activitypub::identifiers::local_actor_id; use crate::activitypub::identifiers::local_actor_id;
use crate::database::{ use crate::database::{
json_macro::{json_from_sql, json_to_sql}, json_macro::{json_from_sql, json_to_sql},
@ -23,6 +23,7 @@ use crate::identity::{
signatures::{PROOF_TYPE_ID_EIP191, PROOF_TYPE_ID_MINISIGN}, signatures::{PROOF_TYPE_ID_EIP191, PROOF_TYPE_ID_MINISIGN},
}; };
use crate::utils::caip2::ChainId; use crate::utils::caip2::ChainId;
use crate::webfinger::types::ActorAddress;
use super::validators::{ use super::validators::{
validate_username, validate_username,
validate_display_name, validate_display_name,

View file

@ -1,7 +1,15 @@
/// https://webfinger.net/ /// https://webfinger.net/
use std::fmt;
use std::str::FromStr;
use regex::Regex;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::errors::ValidationError;
// See also: USERNAME_RE in models::profiles::validators
const ACTOR_ADDRESS_RE: &str = r"(?P<username>[\w\.-]+)@(?P<hostname>[\w\.-]+)";
pub const JRD_CONTENT_TYPE: &str = "application/jrd+json"; pub const JRD_CONTENT_TYPE: &str = "application/jrd+json";
#[derive(Deserialize)] #[derive(Deserialize)]
@ -9,6 +17,43 @@ pub struct WebfingerQueryParams {
pub resource: String, pub resource: String,
} }
pub struct ActorAddress {
pub username: String,
pub hostname: String,
}
impl ActorAddress {
/// Returns acct string, as used in Mastodon
pub fn acct(&self, local_hostname: &str) -> String {
if self.hostname == local_hostname {
self.username.clone()
} else {
self.to_string()
}
}
}
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(),
hostname: caps["hostname"].to_string(),
};
Ok(actor_address)
}
}
impl fmt::Display for ActorAddress {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}@{}", self.username, self.hostname)
}
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Link { pub struct Link {
pub rel: String, pub rel: String,
@ -25,3 +70,17 @@ pub struct JsonResourceDescriptor {
pub subject: String, pub subject: String,
pub links: Vec<Link>, pub links: Vec<Link>,
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_actor_address_parse_address() {
let value = "user_1@example.com";
let actor_address: ActorAddress = value.parse().unwrap();
assert_eq!(actor_address.username, "user_1");
assert_eq!(actor_address.hostname, "example.com");
assert_eq!(actor_address.to_string(), value);
}
}

View file

@ -1,7 +1,6 @@
use actix_web::{get, web, HttpResponse}; use actix_web::{get, web, HttpResponse};
use tokio_postgres::GenericClient; use tokio_postgres::GenericClient;
use crate::activitypub::actors::types::ActorAddress;
use crate::activitypub::constants::AP_MEDIA_TYPE; use crate::activitypub::constants::AP_MEDIA_TYPE;
use crate::activitypub::identifiers::{ use crate::activitypub::identifiers::{
local_actor_id, local_actor_id,
@ -12,10 +11,11 @@ use crate::database::{get_database_client, DbPool};
use crate::errors::{HttpError, ValidationError}; use crate::errors::{HttpError, ValidationError};
use crate::models::users::queries::is_registered_user; use crate::models::users::queries::is_registered_user;
use super::types::{ use super::types::{
JRD_CONTENT_TYPE, ActorAddress,
WebfingerQueryParams,
Link, Link,
JsonResourceDescriptor, JsonResourceDescriptor,
WebfingerQueryParams,
JRD_CONTENT_TYPE,
}; };
// https://datatracker.ietf.org/doc/html/rfc7565#section-7 // https://datatracker.ietf.org/doc/html/rfc7565#section-7