Save extra fields from remote actors

This commit is contained in:
silverpill 2021-09-17 12:36:24 +00:00
parent 6dec1a5da1
commit 7fad429a8c
8 changed files with 60 additions and 16 deletions

View file

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::config::Config;
use crate::errors::HttpError;
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;
@ -14,7 +14,7 @@ use super::views::{
get_followers_url,
get_following_url,
};
use super::vocabulary::{PERSON, IMAGE};
use super::vocabulary::{PERSON, IMAGE, PROPERTY_VALUE};
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
@ -40,6 +40,14 @@ pub struct ActorCapabilities {
accepts_chat_messages: Option<bool>,
}
#[derive(Deserialize, Serialize)]
pub struct ActorProperty {
name: String,
#[serde(rename = "type")]
object_type: String,
value: String,
}
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Actor {
@ -71,12 +79,31 @@ pub struct Actor {
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
pub attachment: Option<Vec<ActorProperty>>,
}
impl Actor {
/// Parse 'attachment' into ExtraField vector
pub fn extra_fields(&self) -> Vec<ExtraField> {
match &self.attachment {
Some(properties) => {
properties.iter()
.map(|prop| ExtraField {
name: prop.name.clone(),
value: prop.value.clone(),
})
.collect()
},
None => vec![],
}
}
}
pub fn get_actor_object(
config: &Config,
user: &User,
) -> Result<Actor, HttpError> {
) -> Result<Actor, rsa::pkcs8::Error> {
let username = &user.profile.username;
let id = get_actor_url(&config.instance_url(), &username);
let inbox = get_inbox_url(&config.instance_url(), &username);
@ -84,15 +111,16 @@ pub fn get_actor_object(
let followers = get_followers_url(&config.instance_url(), &username);
let following = get_following_url(&config.instance_url(), &username);
let private_key = deserialize_private_key(&user.private_key)
.map_err(|_| HttpError::InternalError)?;
let public_key_pem = get_public_key_pem(&private_key)
.map_err(|_| HttpError::InternalError)?;
let private_key = deserialize_private_key(&user.private_key)?;
let public_key_pem = get_public_key_pem(&private_key)?;
let public_key = PublicKey {
id: format!("{}#main-key", id),
owner: id.clone(),
public_key_pem: public_key_pem,
};
let capabilities = ActorCapabilities {
accepts_chat_messages: Some(false),
};
let avatar = match &user.profile.avatar_file_name {
Some(file_name) => {
let image = Image {
@ -113,9 +141,15 @@ pub fn get_actor_object(
},
None => None,
};
let capabilities = ActorCapabilities {
accepts_chat_messages: Some(false),
};
let properties = user.profile.extra_fields.clone()
.unpack().into_iter()
.map(|field| {
ActorProperty {
object_type: PROPERTY_VALUE.to_string(),
name: field.name,
value: field.value,
}
}).collect();
let actor = Actor {
context: Some(json!([
AP_CONTEXT.to_string(),
@ -134,6 +168,7 @@ pub fn get_actor_object(
icon: avatar,
image: banner,
summary: None,
attachment: Some(properties),
};
Ok(actor)
}

View file

@ -92,6 +92,7 @@ pub async fn fetch_profile_by_actor_id(
let actor_value: Value = serde_json::from_str(&actor_json)?;
let actor: Actor = serde_json::from_value(actor_value.clone())?;
let (avatar, banner) = fetch_avatar_and_banner(&actor, media_dir).await?;
let extra_fields = actor.extra_fields();
let actor_address = format!(
"{}@{}",
actor.preferred_username,
@ -102,8 +103,9 @@ pub async fn fetch_profile_by_actor_id(
display_name: Some(actor.name),
acct: actor_address,
bio: actor.summary,
avatar: avatar,
banner: banner,
avatar,
banner,
extra_fields,
actor: Some(actor_value),
};
Ok(profile_data)

View file

@ -146,13 +146,14 @@ pub async fn receive_activity(
let profile = get_profile_by_actor_id(db_client, &actor.id).await?;
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 mut profile_data = ProfileUpdateData {
display_name: Some(actor.name),
bio: actor.summary.clone(),
bio_source: actor.summary,
avatar,
banner,
extra_fields: vec![],
extra_fields,
};
profile_data.clean()?;
update_profile(db_client, &profile.id, profile_data).await?;

View file

@ -47,7 +47,8 @@ async fn get_actor(
) -> Result<HttpResponse, HttpError> {
let db_client = &**get_database_client(&db_pool).await?;
let user = get_user_by_name(db_client, &username).await?;
let actor = get_actor_object(&config, &user)?;
let actor = get_actor_object(&config, &user)
.map_err(|_| HttpError::InternalError)?;
let response = HttpResponse::Ok()
.content_type(ACTIVITY_CONTENT_TYPE)
.json(actor);

View file

@ -12,3 +12,4 @@ pub const PERSON: &str = "Person";
pub const DOCUMENT: &str = "Document";
pub const IMAGE: &str = "Image";
pub const NOTE: &str = "Note";
pub const PROPERTY_VALUE: &str = "PropertyValue";

View file

@ -15,14 +15,15 @@ pub async fn create_profile(
profile_data: &ProfileCreateData,
) -> Result<DbActorProfile, DatabaseError> {
let profile_id = Uuid::new_v4();
let extra_fields = ExtraFields(profile_data.extra_fields.clone());
let result = db_client.query_one(
"
INSERT INTO actor_profile (
id, username, display_name, acct, bio, bio_source,
avatar_file_name, banner_file_name,
avatar_file_name, banner_file_name, extra_fields,
actor_json
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING actor_profile
",
&[
@ -34,6 +35,7 @@ pub async fn create_profile(
&profile_data.bio,
&profile_data.avatar,
&profile_data.banner,
&extra_fields,
&profile_data.actor,
],
).await;

View file

@ -74,6 +74,7 @@ pub struct ProfileCreateData {
pub bio: Option<String>,
pub avatar: Option<String>,
pub banner: Option<String>,
pub extra_fields: Vec<ExtraField>,
pub actor: Option<Value>,
}

View file

@ -145,6 +145,7 @@ pub async fn create_user(
bio: None,
avatar: None,
banner: None,
extra_fields: vec![],
actor: None,
};
let profile = create_profile(&transaction, &profile_data).await?;