From af2f09ea4cbb97d3ee91e30bb58e85508989d63a Mon Sep 17 00:00:00 2001 From: clarkzjw Date: Wed, 26 Jul 2023 12:37:38 -0700 Subject: add example from https://github.com/LemmyNet/activitypub-federation-rust --- fedi/live_federation/objects/person.rs | 140 +++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 fedi/live_federation/objects/person.rs (limited to 'fedi/live_federation/objects/person.rs') diff --git a/fedi/live_federation/objects/person.rs b/fedi/live_federation/objects/person.rs new file mode 100644 index 0000000..d9439ea --- /dev/null +++ b/fedi/live_federation/objects/person.rs @@ -0,0 +1,140 @@ +use crate::{activities::create_post::CreatePost, database::DatabaseHandle, error::Error}; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + http_signatures::generate_actor_keypair, + kinds::actor::PersonType, + protocol::{public_key::PublicKey, verification::verify_domains_match}, + traits::{ActivityHandler, Actor, Object}, +}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use url::Url; + +#[derive(Debug, Clone)] +pub struct DbUser { + pub name: String, + pub ap_id: ObjectId, + pub inbox: Url, + // exists for all users (necessary to verify http signatures) + pub public_key: String, + // exists only for local users + pub private_key: Option, + last_refreshed_at: DateTime, + pub followers: Vec, + pub local: bool, +} + +/// List of all activities which this actor can receive. +#[derive(Deserialize, Serialize, Debug)] +#[serde(untagged)] +#[enum_delegate::implement(ActivityHandler)] +pub enum PersonAcceptedActivities { + CreateNote(CreatePost), +} + +impl DbUser { + pub fn new(hostname: &str, name: &str) -> Result { + let ap_id = Url::parse(&format!("https://{}/{}", hostname, &name))?.into(); + let inbox = Url::parse(&format!("https://{}/{}/inbox", hostname, &name))?; + let keypair = generate_actor_keypair()?; + Ok(DbUser { + name: name.to_string(), + ap_id, + inbox, + public_key: keypair.public_key, + private_key: Some(keypair.private_key), + last_refreshed_at: Utc::now(), + followers: vec![], + local: true, + }) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Person { + #[serde(rename = "type")] + kind: PersonType, + preferred_username: String, + id: ObjectId, + inbox: Url, + public_key: PublicKey, +} + +#[async_trait::async_trait] +impl Object for DbUser { + type DataType = DatabaseHandle; + type Kind = Person; + type Error = Error; + + fn last_refreshed_at(&self) -> Option> { + Some(self.last_refreshed_at) + } + + async fn read_from_id( + object_id: Url, + data: &Data, + ) -> Result, Self::Error> { + let users = data.users.lock().unwrap(); + let res = users + .clone() + .into_iter() + .find(|u| u.ap_id.inner() == &object_id); + Ok(res) + } + + async fn into_json(self, _data: &Data) -> Result { + Ok(Person { + preferred_username: self.name.clone(), + kind: Default::default(), + id: self.ap_id.clone(), + inbox: self.inbox.clone(), + public_key: self.public_key(), + }) + } + + async fn verify( + json: &Self::Kind, + expected_domain: &Url, + _data: &Data, + ) -> Result<(), Self::Error> { + verify_domains_match(json.id.inner(), expected_domain)?; + Ok(()) + } + + async fn from_json( + json: Self::Kind, + _data: &Data, + ) -> Result { + Ok(DbUser { + name: json.preferred_username, + ap_id: json.id, + inbox: json.inbox, + public_key: json.public_key.public_key_pem, + private_key: None, + last_refreshed_at: Utc::now(), + followers: vec![], + local: false, + }) + } +} + +impl Actor for DbUser { + fn id(&self) -> Url { + self.ap_id.inner().clone() + } + + fn public_key_pem(&self) -> &str { + &self.public_key + } + + fn private_key_pem(&self) -> Option { + self.private_key.clone() + } + + fn inbox(&self) -> Url { + self.inbox.clone() + } +} -- cgit v1.2.3