2021-04-09 00:22:17 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
2021-11-18 00:51:56 +00:00
|
|
|
use crate::config::Instance;
|
2021-10-31 20:46:29 +00:00
|
|
|
use crate::errors::ConversionError;
|
|
|
|
use crate::models::profiles::types::{DbActorProfile, ExtraField};
|
2021-04-09 00:22:17 +00:00
|
|
|
use crate::models::users::types::User;
|
|
|
|
use crate::utils::crypto::{deserialize_private_key, get_public_key_pem};
|
|
|
|
use crate::utils::files::get_file_url;
|
|
|
|
use super::constants::AP_CONTEXT;
|
|
|
|
use super::views::{
|
|
|
|
get_actor_url,
|
|
|
|
get_inbox_url,
|
|
|
|
get_outbox_url,
|
|
|
|
get_followers_url,
|
|
|
|
get_following_url,
|
|
|
|
};
|
2021-11-18 00:51:56 +00:00
|
|
|
use super::vocabulary::{IMAGE, PERSON, PROPERTY_VALUE, SERVICE};
|
2021-04-09 00:22:17 +00:00
|
|
|
|
|
|
|
const W3ID_CONTEXT: &str = "https://w3id.org/security/v1";
|
|
|
|
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct PublicKey {
|
|
|
|
id: String,
|
|
|
|
owner: String,
|
|
|
|
pub public_key_pem: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct Image {
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
object_type: String,
|
|
|
|
pub url: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct ActorCapabilities {
|
|
|
|
accepts_chat_messages: Option<bool>,
|
|
|
|
}
|
|
|
|
|
2021-09-17 12:36:24 +00:00
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
pub struct ActorProperty {
|
|
|
|
name: String,
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
object_type: String,
|
|
|
|
value: String,
|
|
|
|
}
|
|
|
|
|
2021-04-09 00:22:17 +00:00
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct Actor {
|
|
|
|
#[serde(rename = "@context")]
|
|
|
|
context: Option<Value>,
|
|
|
|
|
|
|
|
pub id: String,
|
|
|
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
object_type: String,
|
|
|
|
|
|
|
|
pub name: String,
|
|
|
|
pub preferred_username: String,
|
|
|
|
pub inbox: String,
|
|
|
|
pub outbox: String,
|
2021-11-18 00:51:56 +00:00
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub followers: Option<String>,
|
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub following: Option<String>,
|
2021-04-09 00:22:17 +00:00
|
|
|
|
|
|
|
pub public_key: PublicKey,
|
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub capabilities: Option<ActorCapabilities>,
|
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub icon: Option<Image>,
|
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub image: Option<Image>,
|
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub summary: Option<String>,
|
2021-09-17 12:36:24 +00:00
|
|
|
|
2021-11-18 00:51:56 +00:00
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
2021-09-17 12:36:24 +00:00
|
|
|
pub attachment: Option<Vec<ActorProperty>>,
|
2021-12-11 23:25:00 +00:00
|
|
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub url: Option<String>,
|
2021-09-17 12:36:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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(),
|
2021-09-17 14:43:02 +00:00
|
|
|
value_source: None,
|
2021-09-17 12:36:24 +00:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
},
|
|
|
|
None => vec![],
|
|
|
|
}
|
|
|
|
}
|
2021-04-09 00:22:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-31 20:46:29 +00:00
|
|
|
impl DbActorProfile {
|
2021-11-12 23:41:03 +00:00
|
|
|
pub fn remote_actor(&self) -> Result<Option<Actor>, ConversionError> {
|
2021-10-31 20:46:29 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 23:41:03 +00:00
|
|
|
pub type ActorKeyError = rsa::pkcs8::Error;
|
|
|
|
|
|
|
|
pub fn get_local_actor(
|
2021-04-09 00:22:17 +00:00
|
|
|
user: &User,
|
2021-11-12 23:41:03 +00:00
|
|
|
instance_url: &str,
|
|
|
|
) -> Result<Actor, ActorKeyError> {
|
2021-04-09 00:22:17 +00:00
|
|
|
let username = &user.profile.username;
|
2021-12-11 23:25:00 +00:00
|
|
|
let actor_id = get_actor_url(instance_url, username);
|
2021-11-12 23:41:03 +00:00
|
|
|
let inbox = get_inbox_url(instance_url, username);
|
|
|
|
let outbox = get_outbox_url(instance_url, username);
|
|
|
|
let followers = get_followers_url(instance_url, username);
|
|
|
|
let following = get_following_url(instance_url, username);
|
2021-04-09 00:22:17 +00:00
|
|
|
|
2021-09-17 12:36:24 +00:00
|
|
|
let private_key = deserialize_private_key(&user.private_key)?;
|
|
|
|
let public_key_pem = get_public_key_pem(&private_key)?;
|
2021-04-09 00:22:17 +00:00
|
|
|
let public_key = PublicKey {
|
2021-12-11 23:25:00 +00:00
|
|
|
id: format!("{}#main-key", actor_id),
|
|
|
|
owner: actor_id.clone(),
|
2021-04-09 00:22:17 +00:00
|
|
|
public_key_pem: public_key_pem,
|
|
|
|
};
|
2021-09-17 12:36:24 +00:00
|
|
|
let capabilities = ActorCapabilities {
|
|
|
|
accepts_chat_messages: Some(false),
|
|
|
|
};
|
2021-04-09 00:22:17 +00:00
|
|
|
let avatar = match &user.profile.avatar_file_name {
|
|
|
|
Some(file_name) => {
|
|
|
|
let image = Image {
|
|
|
|
object_type: IMAGE.to_string(),
|
2021-11-12 23:41:03 +00:00
|
|
|
url: get_file_url(instance_url, file_name),
|
2021-04-09 00:22:17 +00:00
|
|
|
};
|
|
|
|
Some(image)
|
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
let banner = match &user.profile.banner_file_name {
|
|
|
|
Some(file_name) => {
|
|
|
|
let image = Image {
|
|
|
|
object_type: IMAGE.to_string(),
|
2021-11-12 23:41:03 +00:00
|
|
|
url: get_file_url(instance_url, file_name),
|
2021-04-09 00:22:17 +00:00
|
|
|
};
|
|
|
|
Some(image)
|
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
};
|
2021-09-17 12:36:24 +00:00
|
|
|
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();
|
2021-04-09 00:22:17 +00:00
|
|
|
let actor = Actor {
|
|
|
|
context: Some(json!([
|
|
|
|
AP_CONTEXT.to_string(),
|
|
|
|
W3ID_CONTEXT.to_string(),
|
|
|
|
])),
|
2021-12-11 23:25:00 +00:00
|
|
|
id: actor_id.clone(),
|
2021-04-09 00:22:17 +00:00
|
|
|
object_type: PERSON.to_string(),
|
|
|
|
name: username.to_string(),
|
|
|
|
preferred_username: username.to_string(),
|
|
|
|
inbox,
|
|
|
|
outbox,
|
2021-11-18 00:51:56 +00:00
|
|
|
followers: Some(followers),
|
|
|
|
following: Some(following),
|
2021-04-09 00:22:17 +00:00
|
|
|
public_key,
|
|
|
|
capabilities: Some(capabilities),
|
|
|
|
icon: avatar,
|
|
|
|
image: banner,
|
|
|
|
summary: None,
|
2021-09-17 12:36:24 +00:00
|
|
|
attachment: Some(properties),
|
2021-12-11 23:25:00 +00:00
|
|
|
url: Some(actor_id),
|
2021-04-09 00:22:17 +00:00
|
|
|
};
|
|
|
|
Ok(actor)
|
|
|
|
}
|
2021-11-18 00:51:56 +00:00
|
|
|
|
|
|
|
pub fn get_instance_actor(
|
|
|
|
instance: &Instance,
|
|
|
|
) -> Result<Actor, ActorKeyError> {
|
|
|
|
let actor_id = instance.actor_id();
|
|
|
|
let actor_inbox = format!("{}/inbox", actor_id);
|
|
|
|
let actor_outbox = format!("{}/outbox", actor_id);
|
|
|
|
let public_key_pem = get_public_key_pem(&instance.actor_key)?;
|
|
|
|
let public_key = PublicKey {
|
|
|
|
id: instance.actor_key_id(),
|
|
|
|
owner: actor_id.clone(),
|
|
|
|
public_key_pem: public_key_pem,
|
|
|
|
};
|
|
|
|
let actor = Actor {
|
|
|
|
context: Some(json!([
|
|
|
|
AP_CONTEXT.to_string(),
|
|
|
|
W3ID_CONTEXT.to_string(),
|
|
|
|
])),
|
|
|
|
id: actor_id,
|
|
|
|
object_type: SERVICE.to_string(),
|
|
|
|
name: instance.host(),
|
|
|
|
preferred_username: instance.host(),
|
|
|
|
inbox: actor_inbox,
|
|
|
|
outbox: actor_outbox,
|
|
|
|
followers: None,
|
|
|
|
following: None,
|
|
|
|
public_key,
|
|
|
|
capabilities: None,
|
|
|
|
icon: None,
|
|
|
|
image: None,
|
|
|
|
summary: None,
|
|
|
|
attachment: None,
|
2021-12-11 23:25:00 +00:00
|
|
|
url: None,
|
2021-11-18 00:51:56 +00:00
|
|
|
};
|
|
|
|
Ok(actor)
|
|
|
|
}
|