Reviewed-on: #1129
This commit is contained in:
commit
dd3a5f4a5b
7 changed files with 105 additions and 47 deletions
|
@ -561,7 +561,7 @@ mod tests {
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
||||||
|
|
||||||
static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());
|
static MY_SIGNER: Lazy<MySigner> = Lazy::new(MySigner::new);
|
||||||
|
|
||||||
struct MySigner {
|
struct MySigner {
|
||||||
public_key: String,
|
public_key: String,
|
||||||
|
@ -596,7 +596,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
||||||
verifier.update(data.as_bytes()).unwrap();
|
verifier.update(data.as_bytes()).unwrap();
|
||||||
verifier.verify(&signature).map_err(|_| SignError())
|
verifier.verify(signature).map_err(|_| SignError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,7 +782,7 @@ mod tests {
|
||||||
.done();
|
.done();
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
|
||||||
let res: Result<(), ()> = Inbox::handle(&(), act.clone())
|
let res: Result<(), ()> = Inbox::handle(&(), act)
|
||||||
.with::<FailingActor, Create, MyObject>(None)
|
.with::<FailingActor, Create, MyObject>(None)
|
||||||
.with::<MyActor, Create, MyObject>(None)
|
.with::<MyActor, Create, MyObject>(None)
|
||||||
.done();
|
.done();
|
||||||
|
|
|
@ -518,7 +518,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{ActorAndObjectRef, Create},
|
activity::{ActorAndObjectRef, Create},
|
||||||
object::kind::ArticleType,
|
object::{kind::ArticleType, Image},
|
||||||
|
prelude::{ApActorExt, BaseExt, ExtendsExt, ObjectExt},
|
||||||
};
|
};
|
||||||
use assert_json_diff::assert_json_eq;
|
use assert_json_diff::assert_json_eq;
|
||||||
use serde_json::{from_str, json, to_value};
|
use serde_json::{from_str, json, to_value};
|
||||||
|
@ -592,7 +593,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn de_custom_group() {
|
fn se_custom_group() {
|
||||||
let group = CustomGroup::new(
|
let group = CustomGroup::new(
|
||||||
ApActor::new("https://example.com/inbox".parse().unwrap(), Group::new()),
|
ApActor::new("https://example.com/inbox".parse().unwrap(), Group::new()),
|
||||||
ApSignature {
|
ApSignature {
|
||||||
|
@ -625,6 +626,71 @@ mod tests {
|
||||||
assert_eq!(to_value(group).unwrap(), expected);
|
assert_eq!(to_value(group).unwrap(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn de_custom_group() {
|
||||||
|
let value: CustomGroup = from_str(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"type": "Image"
|
||||||
|
},
|
||||||
|
"id": "https://plume01.localhost/~/Plume01%20Blog%202/",
|
||||||
|
"image": {
|
||||||
|
"type": "Image"
|
||||||
|
},
|
||||||
|
"inbox": "https://plume01.localhost/~/Plume01%20Blog%202/inbox",
|
||||||
|
"name": "Plume01 Blog 2",
|
||||||
|
"outbox": "https://plume01.localhost/~/Plume01%20Blog%202/outbox",
|
||||||
|
"preferredUsername": "Plume01 Blog 2",
|
||||||
|
"publicKey": {
|
||||||
|
"id": "https://plume01.localhost/~/Plume01%20Blog%202/#main-key",
|
||||||
|
"owner": "https://plume01.localhost/~/Plume01%20Blog%202/",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPGtKkl/iMsNAyeVaJGz\noEz5PoNkjRnKK7G97MFvb4zw9zs5SpzWW7b/pKHa4dODcGDJXmkCJ1H5JWyguzN8\n2GNoFjtEOJHxEGwBHSYDsTmhuLNB0DKxMU2iu55g8iIiXhZiIW1FBNGs/Geaymvr\nh/TEtzdReN8wzloRR55kOVcU49xBkqx8cfDSk/lrrDLlpveHdqgaFnIvuw2vycK0\nxFzS3xlEUpzJk9kHxoR1uEAfZ+gCv26Sgo/HqOAhqSD5IU3QZC3kdkr/hwVqtr8U\nXGkGG6Mo1rgzhkYiCFkWrV2WoKkcEHD4nEzbgoZZ5MyuSoloxnyF3NiScqmqW+Yx\nkQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"content": "",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"summary": "",
|
||||||
|
"type": "Group"
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
).unwrap();
|
||||||
|
let mut expected = CustomGroup::new(
|
||||||
|
ApActor::new("https://plume01.localhost/~/Plume01%20Blog%202/inbox".parse().unwrap(), Group::new()),
|
||||||
|
ApSignature {
|
||||||
|
public_key: PublicKey {
|
||||||
|
id: "https://plume01.localhost/~/Plume01%20Blog%202/#main-key".parse().unwrap(),
|
||||||
|
owner: "https://plume01.localhost/~/Plume01%20Blog%202/".parse().unwrap(),
|
||||||
|
public_key_pem: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPGtKkl/iMsNAyeVaJGz\noEz5PoNkjRnKK7G97MFvb4zw9zs5SpzWW7b/pKHa4dODcGDJXmkCJ1H5JWyguzN8\n2GNoFjtEOJHxEGwBHSYDsTmhuLNB0DKxMU2iu55g8iIiXhZiIW1FBNGs/Geaymvr\nh/TEtzdReN8wzloRR55kOVcU49xBkqx8cfDSk/lrrDLlpveHdqgaFnIvuw2vycK0\nxFzS3xlEUpzJk9kHxoR1uEAfZ+gCv26Sgo/HqOAhqSD5IU3QZC3kdkr/hwVqtr8U\nXGkGG6Mo1rgzhkYiCFkWrV2WoKkcEHD4nEzbgoZZ5MyuSoloxnyF3NiScqmqW+Yx\nkQIDAQAB\n-----END PUBLIC KEY-----\n".into(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SourceProperty {
|
||||||
|
source: Source {
|
||||||
|
content: String::from(""),
|
||||||
|
media_type: String::from("text/markdown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expected.set_icon(Image::new().into_any_base().unwrap());
|
||||||
|
expected.set_id(
|
||||||
|
"https://plume01.localhost/~/Plume01%20Blog%202/"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
expected.set_image(Image::new().into_any_base().unwrap());
|
||||||
|
expected.set_name("Plume01 Blog 2");
|
||||||
|
expected.set_outbox(
|
||||||
|
"https://plume01.localhost/~/Plume01%20Blog%202/outbox"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
expected.set_preferred_username("Plume01 Blog 2");
|
||||||
|
expected.set_summary("");
|
||||||
|
|
||||||
|
assert_json_eq!(value, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn se_licensed_article() {
|
fn se_licensed_article() {
|
||||||
let object = ApObject::new(Article::new());
|
let object = ApObject::new(Article::new());
|
||||||
|
|
|
@ -253,7 +253,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
||||||
verifier.update(data.as_bytes()).unwrap();
|
verifier.update(data.as_bytes()).unwrap();
|
||||||
verifier.verify(&signature).map_err(|_| Error())
|
verifier.verify(signature).map_err(|_| Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ mod tests {
|
||||||
let signer = MySigner::new();
|
let signer = MySigner::new();
|
||||||
let headers = HeaderMap::new();
|
let headers = HeaderMap::new();
|
||||||
let result = signature(&signer, &headers, ("post", "/inbox", None)).unwrap();
|
let result = signature(&signer, &headers, ("post", "/inbox", None)).unwrap();
|
||||||
let fields: Vec<&str> = result.to_str().unwrap().split(",").collect();
|
let fields: Vec<&str> = result.to_str().unwrap().split(',').collect();
|
||||||
assert_eq!(r#"headers="(request-target)""#, fields[2]);
|
assert_eq!(r#"headers="(request-target)""#, fields[2]);
|
||||||
let sign = &fields[3][11..(fields[3].len() - 1)];
|
let sign = &fields[3][11..(fields[3].len() - 1)];
|
||||||
assert!(signer.verify("post /inbox", sign.as_bytes()).is_ok());
|
assert!(signer.verify("post /inbox", sign.as_bytes()).is_ok());
|
||||||
|
|
|
@ -18,10 +18,13 @@ use openssl::{
|
||||||
rsa::Rsa,
|
rsa::Rsa,
|
||||||
sign::{Signer, Verifier},
|
sign::{Signer, Verifier},
|
||||||
};
|
};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::{
|
||||||
inbox::{AsActor, FromId},
|
activity_pub::{
|
||||||
sign, ActivityStream, ApSignature, CustomGroup, Id, IntoId, PublicKey, Source, SourceProperty,
|
inbox::{AsActor, FromId},
|
||||||
ToAsString, ToAsUri,
|
sign, ActivityStream, ApSignature, CustomGroup, Id, IntoId, PublicKey, Source,
|
||||||
|
SourceProperty, ToAsString, ToAsUri,
|
||||||
|
},
|
||||||
|
utils::iri_percent_encode_seg,
|
||||||
};
|
};
|
||||||
use webfinger::*;
|
use webfinger::*;
|
||||||
|
|
||||||
|
@ -83,9 +86,13 @@ impl Blog {
|
||||||
|
|
||||||
if inserted.fqn.is_empty() {
|
if inserted.fqn.is_empty() {
|
||||||
if instance.local {
|
if instance.local {
|
||||||
inserted.fqn = inserted.actor_id.clone();
|
inserted.fqn = iri_percent_encode_seg(&inserted.actor_id.clone());
|
||||||
} else {
|
} else {
|
||||||
inserted.fqn = format!("{}@{}", inserted.actor_id, instance.public_domain);
|
inserted.fqn = format!(
|
||||||
|
"{}@{}",
|
||||||
|
iri_percent_encode_seg(&inserted.actor_id),
|
||||||
|
instance.public_domain
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +173,7 @@ impl Blog {
|
||||||
|
|
||||||
pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> {
|
pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> {
|
||||||
let mut blog = ApActor::new(self.inbox_url.parse()?, Group::new());
|
let mut blog = ApActor::new(self.inbox_url.parse()?, Group::new());
|
||||||
blog.set_preferred_username(self.actor_id.clone());
|
blog.set_preferred_username(iri_percent_encode_seg(&self.actor_id));
|
||||||
blog.set_name(self.title.clone());
|
blog.set_name(self.title.clone());
|
||||||
blog.set_outbox(self.outbox_url.parse()?);
|
blog.set_outbox(self.outbox_url.parse()?);
|
||||||
blog.set_summary(self.summary_html.to_string());
|
blog.set_summary(self.summary_html.to_string());
|
||||||
|
@ -381,6 +388,7 @@ impl FromId<DbConn> for Blog {
|
||||||
.ok_or(Error::MissingApProperty)?
|
.ok_or(Error::MissingApProperty)?
|
||||||
.to_string();
|
.to_string();
|
||||||
if name.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
if name.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
||||||
|
tracing::error!("preferredUsername includes invalid character(s): {}", &name);
|
||||||
return Err(Error::InvalidValue);
|
return Err(Error::InvalidValue);
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, result::Error::NotFound, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, result::Error::NotFound, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use plume_common::utils::{md_to_html, iri_percent_encode_seg};
|
use plume_common::utils::{iri_percent_encode_seg, md_to_html};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
#[derive(Clone, Identifiable, Queryable)]
|
#[derive(Clone, Identifiable, Queryable)]
|
||||||
|
|
|
@ -45,7 +45,10 @@ impl Actor for RemoteFetchActor {
|
||||||
RemoteUserFound(user) => match self.conn.get() {
|
RemoteUserFound(user) => match self.conn.get() {
|
||||||
Ok(conn) => {
|
Ok(conn) => {
|
||||||
let conn = DbConn(conn);
|
let conn = DbConn(conn);
|
||||||
if user.get_instance(&conn).map_or(false, |instance| instance.blocked) {
|
if user
|
||||||
|
.get_instance(&conn)
|
||||||
|
.map_or(false, |instance| instance.blocked)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Don't call these functions in parallel
|
// Don't call these functions in parallel
|
||||||
|
|
|
@ -246,20 +246,7 @@ impl User {
|
||||||
fn fetch(url: &str) -> Result<CustomPerson> {
|
fn fetch(url: &str) -> Result<CustomPerson> {
|
||||||
let res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
// without this workaround, publicKey is not correctly deserialized
|
let json = serde_json::from_str::<CustomPerson>(text)?;
|
||||||
let ap_sign = serde_json::from_str::<ApSignature>(text)?;
|
|
||||||
let person = serde_json::from_str::<Person>(text)?;
|
|
||||||
let json = CustomPerson::new(
|
|
||||||
ApActor::new(
|
|
||||||
person
|
|
||||||
.clone()
|
|
||||||
.id_unchecked()
|
|
||||||
.ok_or(Error::MissingApProperty)?
|
|
||||||
.to_owned(),
|
|
||||||
person,
|
|
||||||
),
|
|
||||||
ap_sign,
|
|
||||||
); // FIXME: Don't clone()
|
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,23 +256,13 @@ impl User {
|
||||||
|
|
||||||
pub fn refetch(&self, conn: &Connection) -> Result<()> {
|
pub fn refetch(&self, conn: &Connection) -> Result<()> {
|
||||||
User::fetch(&self.ap_url.clone()).and_then(|json| {
|
User::fetch(&self.ap_url.clone()).and_then(|json| {
|
||||||
let avatar = Media::save_remote(
|
let avatar = json
|
||||||
conn,
|
.icon()
|
||||||
json.ap_actor_ref()
|
.and_then(|icon| icon.iter().next())
|
||||||
.icon()
|
.and_then(|i| i.clone().extend::<Image, ImageType>().ok())
|
||||||
.ok_or(Error::MissingApProperty)? // FIXME: Fails when icon is not set
|
.and_then(|image| image)
|
||||||
.iter()
|
.and_then(|image| image.id_unchecked().map(|url| url.to_string()))
|
||||||
.next()
|
.and_then(|url| Media::save_remote(conn, url, self).ok());
|
||||||
.and_then(|i| {
|
|
||||||
i.clone()
|
|
||||||
.extend::<Image, ImageType>() // FIXME: Don't clone()
|
|
||||||
.ok()?
|
|
||||||
.and_then(|url| Some(url.id_unchecked()?.to_string()))
|
|
||||||
})
|
|
||||||
.ok_or(Error::MissingApProperty)?,
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let pub_key = &json.ext_one.public_key.public_key_pem;
|
let pub_key = &json.ext_one.public_key.public_key_pem;
|
||||||
diesel::update(self)
|
diesel::update(self)
|
||||||
|
@ -960,6 +937,10 @@ impl FromId<DbConn> for User {
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
||||||
|
tracing::error!(
|
||||||
|
"preferredUsername includes invalid character(s): {}",
|
||||||
|
&username
|
||||||
|
);
|
||||||
return Err(Error::InvalidValue);
|
return Err(Error::InvalidValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue