Convert actor_json value into Actor type when reading from database

This commit is contained in:
silverpill 2021-12-31 15:29:44 +00:00
parent 97d798eeed
commit 9156dec5bb
10 changed files with 70 additions and 83 deletions

View file

@ -179,11 +179,11 @@ pub fn create_note(
let mut recipients = vec![AP_PUBLIC.to_string()];
let mut tags = vec![];
for profile in &post.mentions {
let actor_id = profile.actor_id(instance_url).unwrap();
let actor_id = profile.actor_id(instance_url);
if !profile.is_local() {
recipients.push(actor_id);
};
let actor_url = profile.actor_url(instance_url).unwrap();
let actor_url = profile.actor_url(instance_url);
let tag = Tag {
name: format!("@{}", profile.actor_address(instance_host)),
tag_type: MENTION.to_string(),
@ -208,7 +208,7 @@ pub fn create_note(
Some(get_object_url(instance_url, &post.id))
} else {
// Replying to remote post
let remote_actor_id = post.author.actor_id(instance_url).unwrap();
let remote_actor_id = post.author.actor_id(instance_url);
if !recipients.contains(&remote_actor_id) {
recipients.push(remote_actor_id);
};
@ -294,7 +294,7 @@ pub fn create_activity_announce(
repost_id: &Uuid,
) -> Activity {
let object_id = post.get_object_id(instance_url);
let recipient_id = post.author.actor_id(instance_url).unwrap();
let recipient_id = post.author.actor_id(instance_url);
let activity = create_activity(
instance_url,
&actor_profile.username,
@ -345,7 +345,7 @@ pub fn create_activity_delete_note(
};
let mut recipients = vec![AP_PUBLIC.to_string()];
for profile in &post.mentions {
let actor_id = profile.actor_id(instance_url).unwrap();
let actor_id = profile.actor_id(instance_url);
if !profile.is_local() {
recipients.push(actor_id);
};
@ -460,6 +460,7 @@ pub fn create_activity_update_person(
#[cfg(test)]
mod tests {
use crate::activitypub::actor::Actor;
use super::*;
const INSTANCE_HOST: &str = "example.com";
@ -511,10 +512,11 @@ mod tests {
let parent_author_actor_url = "https://test.net/@test";
let parent_author = DbActorProfile {
acct: parent_author_acct.to_string(),
actor_json: Some(json!({
"id": parent_author_actor_id,
"url": parent_author_actor_url,
})),
actor_json: Some(Actor {
id: parent_author_actor_id.to_string(),
url: Some(parent_author_actor_url.to_string()),
..Default::default()
}),
..Default::default()
};
let parent = Post {

View file

@ -2,8 +2,7 @@ use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::config::Instance;
use crate::errors::ConversionError;
use crate::models::profiles::types::{DbActorProfile, ExtraField};
use crate::models::profiles::types::ExtraField;
use crate::models::users::types::User;
use crate::utils::crypto::{deserialize_private_key, get_public_key_pem};
use crate::utils::files::get_file_url;
@ -19,7 +18,8 @@ use super::vocabulary::{IMAGE, PERSON, PROPERTY_VALUE, SERVICE};
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
#[cfg_attr(test, derive(Default))]
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
id: String,
@ -27,7 +27,7 @@ pub struct PublicKey {
pub public_key_pem: String,
}
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Image {
#[serde(rename = "type")]
@ -35,13 +35,13 @@ pub struct Image {
pub url: String,
}
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ActorCapabilities {
accepts_chat_messages: Option<bool>,
}
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
pub struct ActorProperty {
name: String,
#[serde(rename = "type")]
@ -49,16 +49,18 @@ pub struct ActorProperty {
value: Option<String>,
}
#[derive(Deserialize, Serialize)]
// Clone trait is required by FromSql
#[derive(Clone, Deserialize, Serialize)]
#[cfg_attr(test, derive(Default))]
#[serde(rename_all = "camelCase")]
pub struct Actor {
#[serde(rename = "@context")]
context: Option<Value>,
pub context: Option<Value>,
pub id: String,
#[serde(rename = "type")]
object_type: String,
pub object_type: String,
pub name: Option<String>,
@ -116,20 +118,6 @@ impl Actor {
}
}
impl DbActorProfile {
pub fn remote_actor(&self) -> Result<Option<Actor>, ConversionError> {
let actor = match self.actor_json {
Some(ref value) => {
let actor: Actor = serde_json::from_value(value.clone())
.map_err(|_| ConversionError)?;
Some(actor)
},
None => None,
};
Ok(actor)
}
}
pub struct ActorAddress {
pub username: String,
pub instance: String,

View file

@ -53,7 +53,7 @@ async fn send_activity(
.body(activity_json.to_owned());
if instance.is_private {
log::debug!(
log::info!(
"private mode: not sending activity to {}",
inbox_url,
);

View file

@ -472,7 +472,7 @@ pub async fn receive_activity(
&config.media_dir(),
&activity.actor,
).await?;
let source_actor = source_profile.remote_actor().ok().flatten()
let source_actor = source_profile.actor_json
.ok_or(HttpError::InternalError)?;
let target_actor_id = get_object_id(activity.object)?;
let target_username = parse_actor_id(&config.instance_url(), &target_actor_id)?;
@ -543,8 +543,7 @@ pub async fn receive_activity(
let (avatar, banner) = fetch_avatar_and_banner(&actor, &config.media_dir()).await
.map_err(|_| ValidationError("failed to fetch image"))?;
let extra_fields = actor.extra_fields();
let actor_old = profile.remote_actor()
.map_err(|_| HttpError::InternalError)?.unwrap();
let actor_old = profile.actor_json.unwrap();
if actor_old.id != actor.id {
log::warn!(
"actor ID changed from {} to {}",

View file

@ -134,7 +134,7 @@ pub async fn verify_http_signature(
return Err(VerificationError::ActorError(other_error.to_string()));
},
};
let actor = actor_profile.remote_actor().ok().flatten()
let actor = actor_profile.actor_json.as_ref()
.ok_or(VerificationError::ActorError("invalid profile".to_string()))?;
let public_key = deserialize_public_key(&actor.public_key.public_key_pem)?;
@ -146,8 +146,7 @@ pub async fn verify_http_signature(
if !is_valid_signature {
return Err(VerificationError::InvalidSignature);
}
let signer_id = actor_profile.actor_id(&config.instance_url())
.map_err(|_| VerificationError::ActorError("invalid profile".to_string()))?;
let signer_id = actor_profile.actor_id(&config.instance_url());
Ok(signer_id)
}

View file

@ -157,9 +157,7 @@ async fn update_credentials(
let followers = get_followers(db_client, &current_user.id).await?;
let mut recipients: Vec<Actor> = Vec::new();
for follower in followers {
let maybe_remote_actor = follower.remote_actor()
.map_err(|_| HttpError::InternalError)?;
if let Some(remote_actor) = maybe_remote_actor {
if let Some(remote_actor) = follower.actor_json {
recipients.push(remote_actor);
};
};
@ -203,9 +201,7 @@ async fn follow_account(
let db_client = &mut **get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).await?;
let target = get_profile_by_id(db_client, &account_id).await?;
let maybe_remote_actor = target.remote_actor()
.map_err(|_| HttpError::InternalError)?;
if let Some(remote_actor) = maybe_remote_actor {
if let Some(remote_actor) = target.actor_json {
// Remote follow
let request = create_follow_request(db_client, &current_user.id, &target.id).await?;
let activity = create_activity_follow(
@ -236,9 +232,7 @@ async fn unfollow_account(
let db_client = &mut **get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).await?;
let target = get_profile_by_id(db_client, &account_id).await?;
let maybe_remote_actor = target.remote_actor()
.map_err(|_| HttpError::InternalError)?;
if let Some(remote_actor) = maybe_remote_actor {
if let Some(remote_actor) = target.actor_json {
// Remote follow
let follow_request = get_follow_request_by_path(
db_client,

View file

@ -21,8 +21,7 @@ pub async fn get_note_audience(
audience.extend(post.mentions.clone());
let mut recipients: Vec<Actor> = Vec::new();
for profile in audience {
let maybe_remote_actor = profile.remote_actor()?;
if let Some(remote_actor) = maybe_remote_actor {
if let Some(remote_actor) = profile.actor_json {
recipients.push(remote_actor);
};
};
@ -40,10 +39,9 @@ pub async fn get_like_audience(
) -> Result<Audience, DatabaseError> {
let mut recipients: Vec<Actor> = Vec::new();
let mut primary_recipient = None;
let maybe_remote_author = post.author.remote_actor()?;
if let Some(remote_actor) = maybe_remote_author {
if let Some(remote_actor) = post.author.actor_json.as_ref() {
primary_recipient = Some(remote_actor.id.clone());
recipients.push(remote_actor);
recipients.push(remote_actor.clone());
};
Ok(Audience { recipients, primary_recipient })
}
@ -56,16 +54,14 @@ pub async fn get_announce_audience(
let followers = get_followers(db_client, &current_user.id).await?;
let mut recipients: Vec<Actor> = Vec::new();
for profile in followers {
let maybe_remote_actor = profile.remote_actor()?;
if let Some(remote_actor) = maybe_remote_actor {
if let Some(remote_actor) = profile.actor_json {
recipients.push(remote_actor);
};
};
let mut primary_recipient = None;
let maybe_remote_author = post.author.remote_actor()?;
if let Some(remote_actor) = maybe_remote_author {
if let Some(remote_actor) = post.author.actor_json.as_ref() {
primary_recipient = Some(remote_actor.id.clone());
recipients.push(remote_actor);
recipients.push(remote_actor.clone());
};
Ok(Audience { recipients, primary_recipient })
}

View file

@ -22,7 +22,7 @@ impl Mention {
id: profile.id.to_string(),
username: profile.username.clone(),
acct: profile.acct.clone(),
url: profile.actor_id(instance_url).unwrap(),
url: profile.actor_id(instance_url),
}
}
}

View file

@ -69,7 +69,7 @@ pub fn replace_mentions(
if let Some(profile) = mention_map.get(&acct) {
// Replace with a link to profile.
// Actor URL may differ from actor ID.
let url = profile.actor_url(instance_url).unwrap();
let url = profile.actor_url(instance_url);
return format!(
// https://microformats.org/wiki/h-card
r#"{}<span class="h-card"><a class="u-url mention" href="{}">@{}</a></span>{}"#,
@ -103,7 +103,7 @@ pub fn mention_to_address(
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::activitypub::actor::Actor;
use super::*;
const INSTANCE_HOST: &str = "server1.com";
@ -137,10 +137,11 @@ mod tests {
// Remote actor
let profile_2 = DbActorProfile {
username: "user2".to_string(),
actor_json: Some(json!({
"id": "https://server2.com/actors/user2",
"url": "https://server2.com/@user2",
})),
actor_json: Some(Actor {
id: "https://server2.com/actors/user2".to_string(),
url: Some("https://server2.com/@user2".to_string()),
..Default::default()
}),
..Default::default()
};
let text = concat!(

View file

@ -8,8 +8,9 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use crate::activitypub::actor::Actor;
use crate::activitypub::views::get_actor_url;
use crate::errors::{ConversionError, ValidationError};
use crate::errors::ValidationError;
use crate::utils::html::clean_html;
use super::validators::{
validate_username,
@ -41,7 +42,7 @@ impl<'a> FromSql<'a> for ExtraFields {
let fields: Self = serde_json::from_value(json_value)?;
Ok(fields)
}
accepts!(JSON,JSONB);
accepts!(JSON, JSONB);
}
impl ToSql for ExtraFields {
@ -54,6 +55,15 @@ impl ToSql for ExtraFields {
to_sql_checked!();
}
impl<'a> FromSql<'a> for Actor {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, SqlError> {
let Json(json_value) = Json::<Value>::from_sql(ty, raw)?;
let actor: Self = serde_json::from_value(json_value)?;
Ok(actor)
}
accepts!(JSON, JSONB);
}
#[derive(Clone, FromSql)]
#[postgres(name = "actor_profile")]
pub struct DbActorProfile {
@ -70,7 +80,7 @@ pub struct DbActorProfile {
pub following_count: i32,
pub post_count: i32,
pub created_at: DateTime<Utc>,
pub actor_json: Option<Value>,
pub actor_json: Option<Actor>,
}
impl DbActorProfile {
@ -78,22 +88,17 @@ impl DbActorProfile {
self.actor_json.is_none()
}
pub fn actor_id(&self, instance_url: &str) -> Result<String, ConversionError> {
let actor_id = match self.actor_json {
Some(ref actor_value) => {
actor_value["id"].as_str()
.ok_or(ConversionError)?
.to_string()
},
pub fn actor_id(&self, instance_url: &str) -> String {
match self.actor_json {
Some(ref actor) => actor.id.clone(),
None => get_actor_url(instance_url, &self.username),
};
Ok(actor_id)
}
}
pub fn actor_url(&self, instance_url: &str) -> Result<String, ConversionError> {
if let Some(ref actor_value) = self.actor_json {
if let Some(actor_url) = actor_value["url"].as_str() {
return Ok(actor_url.to_string());
pub fn actor_url(&self, instance_url: &str) -> String {
if let Some(ref actor) = self.actor_json {
if let Some(ref actor_url) = actor.url {
return actor_url.to_string();
};
};
self.actor_id(instance_url)
@ -189,7 +194,7 @@ impl ProfileUpdateData {
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::activitypub::actor::Actor;
use super::*;
const INSTANCE_HOST: &str = "example.com";
@ -211,7 +216,10 @@ mod tests {
fn test_remote_actor_address() {
let remote_profile = DbActorProfile {
acct: "test@remote.com".to_string(),
actor_json: Some(json!({"id": "https://test"})),
actor_json: Some(Actor {
id: "https://test".to_string(),
..Default::default()
}),
..Default::default()
};
assert_eq!(