hyaenidae/profiles/src/apub/actions/apub/person.rs
asonix 29bdf064e9 Profiles: Update profile delete to profile suspend
Clear profile data on suspend
Clear comment body on delete
Update Unfollow and Unblock operations to only delete apub IDs if present
2021-01-14 20:41:53 -06:00

200 lines
5.8 KiB
Rust

use crate::{
apub::actions::{CreateProfile, CreateProfileApub, DeleteProfile, UpdateProfile},
apub::ExtendedPerson,
recover,
store::OwnerSource,
Action, Context, Error, RecoverableError, Required,
};
use activitystreams::{base::AnyBase, object::Image, prelude::*, primitives::OneOrMany, public};
use url::Url;
use uuid::Uuid;
fn from_image(
image: Option<&OneOrMany<AnyBase>>,
missing_files: &mut Vec<Url>,
ctx: &Context,
) -> Result<Option<Uuid>, Error> {
let image = if let Some(image) = image.and_then(|i| i.as_one()) {
image
} else {
return Ok(None);
};
let image_opt = Image::from_any_base(image.clone())?;
let url_opt =
image_opt.and_then(|i| i.url().and_then(|u| u.as_single_id()).map(|u| u.to_owned()));
let url = if let Some(url) = url_opt {
url
} else {
return Ok(None);
};
match ctx.apub.id_for_apub(&url)? {
Some(uuid) => Ok(Some(uuid.file().req()?)),
None => {
missing_files.push(url);
Ok(None)
}
}
}
pub(crate) fn person(
person: &ExtendedPerson,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let id = person.id_unchecked().req()?;
// Double create
if ctx.apub.object(id)?.is_some() {
return Err(Error::Invalid);
}
let mut missing_files = Vec::new();
let banner = from_image(person.image(), &mut missing_files, ctx)?;
let icon = from_image(person.icon(), &mut missing_files, ctx)?;
if !missing_files.is_empty() {
return Ok(Err(RecoverableError::MissingImages(missing_files)));
}
let handle = person.preferred_username().req()?.to_owned();
let domain = id.domain().req()?.to_owned();
let display_name = person
.name()
.and_then(|n| n.one())
.and_then(|s| s.as_xsd_string())
.map(|s| s.to_owned());
let description = person
.summary()
.and_then(|s| s.as_single_xsd_string())
.map(|s| s.to_owned());
let public_key = person.ext_one.public_key.public_key_pem.clone();
let public_key_id = person.ext_one.public_key.id.clone();
let published = person.published().req()?.into();
let login_required = person
.to()
.map(|to| {
to.iter()
.find(|entry| entry.id() == Some(&public()))
.is_some()
})
.unwrap_or(true);
Ok(Ok(Box::new(CreateProfile {
apub: Some(CreateProfileApub {
person_apub_id: id.to_owned(),
public_key_id,
public_key,
}),
owner_source: OwnerSource::Remote(id.to_string()),
handle,
domain,
display_name,
description,
login_required,
icon,
banner,
published,
})))
}
pub(crate) fn create_person(
_create: &activitystreams::activity::Create,
person_obj: &ExtendedPerson,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
// TODO: Validate Server Actor
person(person_obj, ctx)
}
pub(crate) fn update_person(
update: &activitystreams::activity::Update,
person: &ExtendedPerson,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let update_actor = update.actor()?.as_single_id().req()?;
let id = person.id_unchecked().req()?;
if update_actor != id {
return Err(Error::Invalid);
}
let profile_id = recover!(id, ctx.apub.id_for_apub(id)?);
let profile_id = profile_id.profile().req()?;
if let Some(actor_profile) = ctx.store.profiles.by_id(profile_id)? {
if actor_profile.is_suspended() {
return Err(Error::Invalid);
}
} else {
return Err(Error::Invalid);
}
let profile_id = recover!(id, ctx.apub.id_for_apub(id)?);
let profile_id = profile_id.profile().req()?;
let mut missing_files = Vec::new();
let banner = from_image(person.image(), &mut missing_files, ctx)?;
let icon = from_image(person.icon(), &mut missing_files, ctx)?;
if !missing_files.is_empty() {
return Ok(Err(RecoverableError::MissingImages(missing_files)));
}
let display_name = person
.name()
.and_then(|n| n.one())
.and_then(|s| s.as_xsd_string())
.map(|s| s.to_owned());
let description = person
.summary()
.and_then(|s| s.as_single_xsd_string())
.map(|s| s.to_owned());
let public_key_id = person.ext_one.public_key.id.clone();
let public_key = person.ext_one.public_key.public_key_pem.clone();
let login_required = person
.to()
.map(|to| {
to.iter()
.find(|entry| entry.id() == Some(&public()))
.is_some()
})
.unwrap_or(true);
Ok(Ok(Box::new(UpdateProfile {
profile_id,
display_name,
description,
login_required: Some(login_required),
icon,
banner,
public_key_id: Some(public_key_id),
public_key: Some(public_key),
})))
}
pub(crate) fn delete_person(
delete: &activitystreams::activity::Delete,
person: &ExtendedPerson,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let delete_actor = delete.actor()?.as_single_id().req()?;
let id = person.id_unchecked().req()?;
// TODO: Validate Server Actor
if delete_actor != id {
return Err(Error::Invalid);
}
let profile_id = recover!(id, ctx.apub.id_for_apub(id)?);
let profile_id = profile_id.profile().req()?;
if let Some(actor_profile) = ctx.store.profiles.by_id(profile_id)? {
if actor_profile.is_suspended() {
return Err(Error::Invalid);
}
} else {
return Err(Error::Invalid);
}
Ok(Ok(Box::new(DeleteProfile { profile_id })))
}