This commit is contained in:
Felix Ableitner 2024-09-11 15:46:19 +02:00
parent d0f37c699f
commit bff0567c27
13 changed files with 56 additions and 35 deletions

View file

@ -12,9 +12,9 @@ use activitypub_federation::{
kinds::activity::CreateType,
protocol::{context::WithContext, helpers::deserialize_one_or_many},
traits::{ActivityHandler, Object},
url::Url,
};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]

View file

@ -6,11 +6,11 @@ use activitypub_federation::{
kinds::actor::PersonType,
protocol::{public_key::PublicKey, verification::verify_domains_match},
traits::{ActivityHandler, Actor, Object},
url::Url,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use url::Url;
use std::{fmt::Debug, str::FromStr};
#[derive(Debug, Clone)]
pub struct DbUser {
@ -36,8 +36,8 @@ pub enum PersonAcceptedActivities {
impl DbUser {
pub fn new(hostname: &str, name: &str) -> Result<DbUser, Error> {
let ap_id = Url::parse(&format!("https://{}/{}", hostname, &name))?.into();
let inbox = Url::parse(&format!("https://{}/{}/inbox", hostname, &name))?;
let ap_id = Url::from_str(&format!("https://{}/{}", hostname, &name))?.into();
let inbox = Url::from_str(&format!("https://{}/{}/inbox", hostname, &name))?;
let keypair = generate_actor_keypair()?;
Ok(DbUser {
name: name.to_string(),

View file

@ -11,10 +11,10 @@ use activitypub_federation::{
kinds::{object::NoteType, public},
protocol::{helpers::deserialize_one_or_many, verification::verify_domains_match},
traits::{Actor, Object},
url::Url,
};
use activitystreams_kinds::link::MentionType;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Debug)]
pub struct DbPost {
@ -63,7 +63,7 @@ impl Object for DbPost {
id: self.ap_id,
content: self.text,
attributed_to: self.creator,
to: vec![public()],
to: vec![public().try_into()?],
tag: vec![],
in_reply_to: None,
})
@ -98,7 +98,7 @@ impl Object for DbPost {
kind: Default::default(),
id: generate_object_id(data.domain())?.into(),
attributed_to: data.local_user().ap_id,
to: vec![public()],
to: vec![public().try_into()?],
content: format!("Hello {}", creator.name),
in_reply_to: Some(json.id.clone()),
tag: vec![mention],

View file

@ -1,5 +1,8 @@
use std::str::FromStr;
use activitypub_federation::url::Url;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use url::{ParseError, Url};
use url::ParseError;
/// Just generate random url as object id. In a real project, you probably want to use
/// an url which contains the database id for easy retrieval (or store the random id in db).
@ -9,5 +12,5 @@ pub fn generate_object_id(domain: &str) -> Result<Url, ParseError> {
.take(7)
.map(char::from)
.collect();
Url::parse(&format!("https://{}/objects/{}", domain, id))
Url::from_str(&format!("https://{}/objects/{}", domain, id))
}

View file

@ -4,9 +4,9 @@ use activitypub_federation::{
fetch::object_id::ObjectId,
kinds::activity::AcceptType,
traits::ActivityHandler,
url::Url,
};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]

View file

@ -9,9 +9,9 @@ use activitypub_federation::{
kinds::activity::CreateType,
protocol::helpers::deserialize_one_or_many,
traits::{ActivityHandler, Object},
url::Url,
};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]

View file

@ -9,9 +9,9 @@ use activitypub_federation::{
fetch::object_id::ObjectId,
kinds::activity::FollowType,
traits::{ActivityHandler, Actor},
url::Url,
};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]

View file

@ -2,21 +2,23 @@ use crate::{
objects::{person::DbUser, post::DbPost},
Error,
};
use activitypub_federation::config::{FederationConfig, UrlVerifier};
use activitypub_federation::{
config::{FederationConfig, UrlVerifier},
url::Url,
};
use anyhow::anyhow;
use async_trait::async_trait;
use std::{
str::FromStr,
sync::{Arc, Mutex},
};
use url::Url;
pub async fn new_instance(
hostname: &str,
name: String,
) -> Result<FederationConfig<DatabaseHandle>, Error> {
let mut system_user = DbUser::new(hostname, "system".into())?;
system_user.ap_id = Url::parse(&format!("http://{}/", hostname))?.into();
system_user.ap_id = Url::from_str(&format!("http://{}/", hostname))?.into();
let local_user = DbUser::new(hostname, name)?;
let database = Arc::new(Database {
@ -51,7 +53,7 @@ struct MyUrlVerifier();
#[async_trait]
impl UrlVerifier for MyUrlVerifier {
async fn verify(&self, url: &Url) -> Result<(), activitypub_federation::error::Error> {
if url.domain() == Some("malicious.com") {
if url.domain() == "malicious.com" {
Err(activitypub_federation::error::Error::Other(
"malicious domain".into(),
))

View file

@ -14,11 +14,11 @@ use activitypub_federation::{
kinds::actor::PersonType,
protocol::{context::WithContext, public_key::PublicKey, verification::verify_domains_match},
traits::{ActivityHandler, Actor, Object},
url::Url,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use url::Url;
use std::{fmt::Debug, str::FromStr};
#[derive(Debug, Clone)]
pub struct DbUser {
@ -46,8 +46,8 @@ pub enum PersonAcceptedActivities {
impl DbUser {
pub fn new(hostname: &str, name: String) -> Result<DbUser, Error> {
let ap_id = Url::parse(&format!("http://{}/{}", hostname, &name))?.into();
let inbox = Url::parse(&format!("http://{}/{}/inbox", hostname, &name))?;
let ap_id = Url::from_str(&format!("http://{}/{}", hostname, &name))?.into();
let inbox = Url::from_str(&format!("http://{}/{}/inbox", hostname, &name))?;
let keypair = generate_actor_keypair()?;
Ok(DbUser {
name,
@ -79,7 +79,7 @@ impl DbUser {
}
pub fn followers_url(&self) -> Result<Url, Error> {
Ok(Url::parse(&format!("{}/followers", self.ap_id.inner()))?)
Ok(Url::from_str(&format!("{}/followers", self.ap_id.inner()))?)
}
pub async fn follow(&self, other: &str, data: &Data<DatabaseHandle>) -> Result<(), Error> {

View file

@ -5,9 +5,9 @@ use activitypub_federation::{
kinds::{object::NoteType, public},
protocol::{helpers::deserialize_one_or_many, verification::verify_domains_match},
traits::Object,
url::Url,
};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Debug)]
pub struct DbPost {
@ -19,7 +19,7 @@ pub struct DbPost {
impl DbPost {
pub fn new(text: String, creator: ObjectId<DbUser>) -> Result<DbPost, Error> {
let ap_id = generate_object_id(creator.inner().domain().unwrap())?.into();
let ap_id = generate_object_id(creator.inner().domain())?.try_into()?;
Ok(DbPost {
text,
ap_id,
@ -65,7 +65,7 @@ impl Object for DbPost {
kind: Default::default(),
id: self.ap_id,
attributed_to: self.creator,
to: vec![public(), creator.followers_url()?],
to: vec![public().try_into()?, creator.followers_url()?],
content: self.text,
})
}

View file

@ -47,6 +47,8 @@ where
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod test {
use std::str::FromStr;
use super::*;
use crate::{
activity_sending::generate_request_headers,
@ -165,7 +167,7 @@ mod test {
actor: ObjectId::parse("http://localhost:123").unwrap(),
object: ObjectId::parse("http://localhost:124").unwrap(),
kind: Default::default(),
id: "http://localhost:123/1".try_into().unwrap(),
id: "http://localhost:123/1".parse().unwrap(),
};
let body: Bytes = serde_json::to_vec(&activity).unwrap().into();
let incoming_request = construct_request(&body, activity.actor.inner()).await;

View file

@ -135,13 +135,13 @@ async fn fetch_object_http_with_accept<T: Clone, Kind: DeserializeOwned>(
match serde_json::from_slice(&text) {
Ok(object) => Ok(FetchObjectResponse {
object,
url: url.into(),
url: url.try_into()?,
content_type,
object_id,
}),
Err(e) => Err(ParseFetchedObject(
e,
url.into(),
url.try_into()?,
String::from_utf8(Vec::from(text))?,
)),
}

View file

@ -1,13 +1,12 @@
//! Wrapper for `url::Url` type.
use serde::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter},
ops::Deref,
str::FromStr,
};
use serde::{Deserialize, Serialize};
/// Wrapper for `url::Url` type. Has `domain` as mandatory field, and prints plain
/// string for debugging.
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
@ -22,6 +21,7 @@ impl Deref for Url {
}
impl Display for Url {
#[allow(clippy::to_string_in_format_args)]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.to_string())
}
@ -30,14 +30,24 @@ impl Display for Url {
impl Url {
/// Returns domain of the url
pub fn domain(&self) -> &str {
// TODO: must have error handling, or ensure at creation that it has domain
self.0.domain().expect("has domain")
}
}
impl From<url::Url> for Url {
fn from(value: url::Url) -> Self {
Url(value)
impl TryFrom<url::Url> for Url {
type Error = url::ParseError;
fn try_from(value: url::Url) -> Result<Self, Self::Error> {
if value.domain().is_none() {
return Err(url::ParseError::EmptyHost);
}
Ok(Url(value))
}
}
#[allow(clippy::from_over_into)]
impl Into<url::Url> for Url {
fn into(self) -> url::Url {
self.0
}
}
@ -45,6 +55,10 @@ impl FromStr for Url {
type Err = url::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(url::Url::from_str(s).map(Url).unwrap())
let url = url::Url::from_str(s)?;
if url.domain().is_none() {
return Err(url::ParseError::EmptyHost);
}
Ok(Url(url))
}
}