]> Untitled Git - lemmy.git/commitdiff
Add http signature to outgoing apub requests
authorFelix <me@nutomic.com>
Sat, 18 Apr 2020 18:54:20 +0000 (20:54 +0200)
committerFelix <me@nutomic.com>
Sat, 18 Apr 2020 18:54:20 +0000 (20:54 +0200)
12 files changed:
server/Cargo.lock
server/Cargo.toml
server/src/api/community.rs
server/src/api/user.rs
server/src/apub/activities.rs
server/src/apub/community_inbox.rs
server/src/apub/fetcher.rs
server/src/apub/mod.rs
server/src/apub/signatures.rs
server/src/db/code_migrations.rs
server/src/db/community.rs
server/src/db/user.rs

index b17f4d60b3959af625743d8ffe7e81e493d5a58c..577c6d3174a5102e27f92d4ddde8894c437c6d30 100644 (file)
@@ -74,7 +74,7 @@ dependencies = [
  "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "trust-dns-resolver 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -126,7 +126,7 @@ dependencies = [
  "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -160,7 +160,7 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytestring 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1176,7 +1176,7 @@ dependencies = [
  "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1237,7 +1237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "http"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1245,6 +1245,15 @@ dependencies = [
  "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "http-signature-normalization"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "httparse"
 version = "1.3.4"
@@ -1314,7 +1323,7 @@ dependencies = [
  "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1387,6 +1396,7 @@ dependencies = [
  "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "actix-web-actors 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "base64 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bcrypt 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "comrak 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1398,6 +1408,8 @@ dependencies = [
  "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http-signature-normalization 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "jsonwebtoken 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3070,7 +3082,8 @@ dependencies = [
 "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e"
 "checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
 "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
-"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b"
+"checksum http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
+"checksum http-signature-normalization 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "257835255b5d40c6de712d90e56dc874ca5da2816121e7b9f3cfc7b3a55a5714"
 "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
 "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
 "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
index 03bbfbee547bc1dc9862635a6027d593c796b387..8bd170efba8e2c7af0fdc12bc6bcd97d5993ff03 100644 (file)
@@ -39,3 +39,6 @@ percent-encoding = "2.1.0"
 isahc = "0.9"
 comrak = "0.7"
 openssl = "0.10"
+http = "0.2.1"
+http-signature-normalization = "0.4.1"
+base64 = "0.12.0"
index 40d8afe39331bbfe0f90ee3957c086a9ea0cb26d..30c8aa2052dc2d5048676112337c266054dfbbc0 100644 (file)
@@ -1,6 +1,7 @@
 use super::*;
 use crate::apub::activities::follow_community;
-use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType};
+use crate::apub::signatures::generate_actor_keypair;
+use crate::apub::{make_apub_endpoint, EndpointType};
 use diesel::PgConnection;
 use std::str::FromStr;
 
@@ -200,7 +201,7 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
     }
 
     // When you create a community, make sure the user becomes a moderator and a follower
-    let (community_public_key, community_private_key) = gen_keypair_str();
+    let keypair = generate_actor_keypair();
 
     let community_form = CommunityForm {
       name: data.name.to_owned(),
@@ -214,8 +215,8 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
       updated: None,
       actor_id: make_apub_endpoint(EndpointType::Community, &data.name).to_string(),
       local: true,
-      private_key: Some(community_private_key),
-      public_key: Some(community_public_key),
+      private_key: Some(keypair.private_key),
+      public_key: Some(keypair.public_key),
       last_refreshed_at: None,
       published: None,
     };
index fbdead53b36e10e5910adfaa5a00163c1fab31dd..35bdd33abcc99de19c820e7b467407fb9c78f035 100644 (file)
@@ -1,5 +1,6 @@
 use super::*;
-use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType};
+use crate::apub::signatures::generate_actor_keypair;
+use crate::apub::{make_apub_endpoint, EndpointType};
 use crate::settings::Settings;
 use crate::{generate_random_string, send_email};
 use bcrypt::verify;
@@ -251,7 +252,7 @@ impl Perform<LoginResponse> for Oper<Register> {
       return Err(APIError::err("admin_already_created").into());
     }
 
-    let (user_public_key, user_private_key) = gen_keypair_str();
+    let keypair = generate_actor_keypair();
 
     // Register the new user
     let user_form = UserForm {
@@ -274,8 +275,8 @@ impl Perform<LoginResponse> for Oper<Register> {
       actor_id: make_apub_endpoint(EndpointType::User, &data.username).to_string(),
       bio: None,
       local: true,
-      private_key: Some(user_private_key),
-      public_key: Some(user_public_key),
+      private_key: Some(keypair.private_key),
+      public_key: Some(keypair.public_key),
       last_refreshed_at: None,
     };
 
@@ -295,7 +296,7 @@ impl Perform<LoginResponse> for Oper<Register> {
       }
     };
 
-    let (community_public_key, community_private_key) = gen_keypair_str();
+    let keypair = generate_actor_keypair();
 
     // Create the main community if it doesn't exist
     let main_community: Community = match Community::read(&conn, 2) {
@@ -314,8 +315,8 @@ impl Perform<LoginResponse> for Oper<Register> {
           updated: None,
           actor_id: make_apub_endpoint(EndpointType::Community, default_community_name).to_string(),
           local: true,
-          private_key: Some(community_private_key),
-          public_key: Some(community_public_key),
+          private_key: Some(keypair.private_key),
+          public_key: Some(keypair.public_key),
           last_refreshed_at: None,
           published: None,
         };
index 73b7293d7647ad88799f9cff6e6c4678ad6123be..a7f63ec857966d0f9ec5666b11d628518e8d2aad 100644 (file)
@@ -1,4 +1,5 @@
 use crate::apub::is_apub_id_valid;
+use crate::apub::signatures::{sign, Keypair};
 use crate::db::community::Community;
 use crate::db::community_view::CommunityFollowerView;
 use crate::db::post::Post;
@@ -14,6 +15,7 @@ use failure::_core::fmt::Debug;
 use isahc::prelude::*;
 use log::debug;
 use serde::Serialize;
+use url::Url;
 
 fn populate_object_props(
   props: &mut ObjectProperties,
@@ -32,18 +34,27 @@ fn populate_object_props(
 }
 
 /// Send an activity to a list of recipients, using the correct headers etc.
-fn send_activity<A>(activity: &A, to: Vec<String>) -> Result<(), Error>
+fn send_activity<A>(
+  activity: &A,
+  keypair: &Keypair,
+  sender_id: &str,
+  to: Vec<String>,
+) -> Result<(), Error>
 where
   A: Serialize + Debug,
 {
   let json = serde_json::to_string(&activity)?;
   debug!("Sending activitypub activity {} to {:?}", json, to);
   for t in to {
-    if !is_apub_id_valid(&t) {
+    let to_url = Url::parse(&t)?;
+    if !is_apub_id_valid(&to_url) {
       debug!("Not sending activity to {} (invalid or blacklisted)", t);
       continue;
     }
-    let res = Request::post(t)
+    let request = Request::post(t).header("Host", to_url.domain().unwrap());
+    let signature = sign(&request, keypair, sender_id)?;
+    let res = request
+      .header("Signature", signature)
       .header("Content-Type", "application/json")
       .body(json.to_owned())?
       .send()?;
@@ -77,7 +88,12 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
     .create_props
     .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
     .set_object_base_box(page)?;
-  send_activity(&create, get_follower_inboxes(conn, &community)?)?;
+  send_activity(
+    &create,
+    &creator.get_keypair().unwrap(),
+    &creator.actor_id,
+    get_follower_inboxes(conn, &community)?,
+  )?;
   Ok(())
 }
 
@@ -95,7 +111,12 @@ pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
     .update_props
     .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
     .set_object_base_box(page)?;
-  send_activity(&update, get_follower_inboxes(conn, &community)?)?;
+  send_activity(
+    &update,
+    &creator.get_keypair().unwrap(),
+    &creator.actor_id,
+    get_follower_inboxes(conn, &community)?,
+  )?;
   Ok(())
 }
 
@@ -116,12 +137,23 @@ pub fn follow_community(
     .set_actor_xsd_any_uri(user.actor_id.clone())?
     .set_object_xsd_any_uri(community.actor_id.clone())?;
   let to = format!("{}/inbox", community.actor_id);
-  send_activity(&follow, vec![to])?;
+  send_activity(
+    &follow,
+    &community.get_keypair().unwrap(),
+    &community.actor_id,
+    vec![to],
+  )?;
   Ok(())
 }
 
 /// As a local community, accept the follow request from a remote user.
-pub fn accept_follow(follow: &Follow) -> Result<(), Error> {
+pub fn accept_follow(follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
+  let community_uri = follow
+    .follow_props
+    .get_actor_xsd_any_uri()
+    .unwrap()
+    .to_string();
+  let community = Community::read_from_actor_id(conn, &community_uri)?;
   let mut accept = Accept::new();
   accept
     .object_props
@@ -137,14 +169,12 @@ pub fn accept_follow(follow: &Follow) -> Result<(), Error> {
   accept
     .accept_props
     .set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
-  let to = format!(
-    "{}/inbox",
-    follow
-      .follow_props
-      .get_actor_xsd_any_uri()
-      .unwrap()
-      .to_string()
-  );
-  send_activity(&accept, vec![to])?;
+  let to = format!("{}/inbox", community_uri);
+  send_activity(
+    &accept,
+    &community.get_keypair().unwrap(),
+    &community.actor_id,
+    vec![to],
+  )?;
   Ok(())
 }
index 65d7bec1ffaa313521d273aca1c0c086faf5ed29..a60d8c68391431e91b5051a54343beb39794abd6 100644 (file)
@@ -56,6 +56,6 @@ fn handle_follow(follow: &Follow, conn: &PgConnection) -> Result<HttpResponse, E
     user_id: user.id,
   };
   CommunityFollower::follow(&conn, &community_follower_form)?;
-  accept_follow(&follow)?;
+  accept_follow(&follow, conn)?;
   Ok(HttpResponse::Ok().finish())
 }
index d44fdcb5ffd3f5905b51d89951d4e62b513651f6..71453a2a493954d9b3cb22c24816e0a7ca7439f0 100644 (file)
@@ -67,7 +67,7 @@ pub fn fetch_remote_object<Response>(url: &Url) -> Result<Response, Error>
 where
   Response: for<'de> Deserialize<'de>,
 {
-  if !is_apub_id_valid(&url.to_string()) {
+  if !is_apub_id_valid(&url) {
     return Err(format_err!("Activitypub uri invalid or blocked: {}", url));
   }
   // TODO: this function should return a future
index 634f351010ea1213f879265564678107aa9d6b01..8d5df8a8b5c93b64aa6d1340f7cba62a91d8dcbf 100644 (file)
@@ -12,7 +12,6 @@ use activitystreams::actor::{properties::ApActorProperties, Group, Person};
 use activitystreams::ext::Ext;
 use actix_web::body::Body;
 use actix_web::HttpResponse;
-use openssl::{pkey::PKey, rsa::Rsa};
 use serde::ser::Serialize;
 use url::Url;
 
@@ -72,31 +71,9 @@ pub fn get_apub_protocol_string() -> &'static str {
   }
 }
 
-/// Generate the asymmetric keypair for ActivityPub HTTP signatures.
-pub fn gen_keypair_str() -> (String, String) {
-  let rsa = Rsa::generate(2048).expect("sign::gen_keypair: key generation error");
-  let pkey = PKey::from_rsa(rsa).expect("sign::gen_keypair: parsing error");
-  let public_key = pkey
-    .public_key_to_pem()
-    .expect("sign::gen_keypair: public key encoding error");
-  let private_key = pkey
-    .private_key_to_pem_pkcs8()
-    .expect("sign::gen_keypair: private key encoding error");
-  (vec_bytes_to_str(public_key), vec_bytes_to_str(private_key))
-}
-
-fn vec_bytes_to_str(bytes: Vec<u8>) -> String {
-  String::from_utf8_lossy(&bytes).into_owned()
-}
-
 // Checks if the ID has a valid format, correct scheme, and is in the whitelist.
-fn is_apub_id_valid(apub_id: &str) -> bool {
-  let url = match Url::parse(apub_id) {
-    Ok(u) => u,
-    Err(_) => return false,
-  };
-
-  if url.scheme() != get_apub_protocol_string() {
+fn is_apub_id_valid(apub_id: &Url) -> bool {
+  if apub_id.scheme() != get_apub_protocol_string() {
     return false;
   }
 
@@ -106,7 +83,7 @@ fn is_apub_id_valid(apub_id: &str) -> bool {
     .split(',')
     .map(|d| d.to_string())
     .collect();
-  match url.domain() {
+  match apub_id.domain() {
     Some(d) => whitelist.contains(&d.to_owned()),
     None => false,
   }
index 0348acb851561035907cac8821d110699cd7bbc5..e0734e7be2535b53a6a760c2ab5e545b1a006d29 100644 (file)
@@ -1,7 +1,69 @@
-// For this example, we'll use the Extensible trait, the Extension trait, the Actor trait, and
-// the Person type
 use activitystreams::{actor::Actor, ext::Extension};
+use failure::Error;
+use http::request::Builder;
+use http_signature_normalization::Config;
+use openssl::hash::MessageDigest;
+use openssl::sign::Signer;
+use openssl::{pkey::PKey, rsa::Rsa};
 use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+
+pub struct Keypair {
+  pub private_key: String,
+  pub public_key: String,
+}
+
+/// Generate the asymmetric keypair for ActivityPub HTTP signatures.
+pub fn generate_actor_keypair() -> Keypair {
+  let rsa = Rsa::generate(2048).expect("sign::gen_keypair: key generation error");
+  let pkey = PKey::from_rsa(rsa).expect("sign::gen_keypair: parsing error");
+  let public_key = pkey
+    .public_key_to_pem()
+    .expect("sign::gen_keypair: public key encoding error");
+  let private_key = pkey
+    .private_key_to_pem_pkcs8()
+    .expect("sign::gen_keypair: private key encoding error");
+  Keypair {
+    private_key: String::from_utf8_lossy(&private_key).into_owned(),
+    public_key: String::from_utf8_lossy(&public_key).into_owned(),
+  }
+}
+
+/// Signs request headers with the given keypair.
+pub fn sign(request: &Builder, keypair: &Keypair, sender_id: &str) -> Result<String, Error> {
+  let signing_key_id = format!("{}#main-key", sender_id);
+  let config = Config::new();
+
+  let headers = request
+    .headers_ref()
+    .unwrap()
+    .iter()
+    .map(|h| -> Result<(String, String), Error> {
+      Ok((h.0.as_str().to_owned(), h.1.to_str()?.to_owned()))
+    })
+    .collect::<Result<BTreeMap<String, String>, Error>>()?;
+
+  let signature_header_value = config
+    .begin_sign(
+      request.method_ref().unwrap().as_str(),
+      request
+        .uri_ref()
+        .unwrap()
+        .path_and_query()
+        .unwrap()
+        .as_str(),
+      headers,
+    )
+    .sign(signing_key_id, |signing_string| {
+      let private_key = PKey::private_key_from_pem(keypair.private_key.as_bytes())?;
+      let mut signer = Signer::new(MessageDigest::sha256(), &private_key).unwrap();
+      signer.update(signing_string.as_bytes()).unwrap();
+      Ok(base64::encode(signer.sign_to_vec()?)) as Result<_, Error>
+    })?
+    .signature_header();
+
+  Ok(signature_header_value)
+}
 
 // The following is taken from here:
 // https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
index a13a99647587ebe5fe6612f217d88c7c17a3a9e4..a72de685e385f690d4c6f3269688387077ecd485 100644 (file)
@@ -4,7 +4,8 @@ use super::community::{Community, CommunityForm};
 use super::post::Post;
 use super::user::{UserForm, User_};
 use super::*;
-use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType};
+use crate::apub::signatures::generate_actor_keypair;
+use crate::apub::{make_apub_endpoint, EndpointType};
 use crate::naive_now;
 use log::info;
 
@@ -29,7 +30,7 @@ fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> {
     .load::<User_>(conn)?;
 
   for cuser in &incorrect_users {
-    let (user_public_key, user_private_key) = gen_keypair_str();
+    let keypair = generate_actor_keypair();
 
     let form = UserForm {
       name: cuser.name.to_owned(),
@@ -51,8 +52,8 @@ fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> {
       actor_id: make_apub_endpoint(EndpointType::User, &cuser.name).to_string(),
       bio: cuser.bio.to_owned(),
       local: cuser.local,
-      private_key: Some(user_private_key),
-      public_key: Some(user_public_key),
+      private_key: Some(keypair.private_key),
+      public_key: Some(keypair.public_key),
       last_refreshed_at: Some(naive_now()),
     };
 
@@ -76,7 +77,7 @@ fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> {
     .load::<Community>(conn)?;
 
   for ccommunity in &incorrect_communities {
-    let (community_public_key, community_private_key) = gen_keypair_str();
+    let keypair = generate_actor_keypair();
 
     let form = CommunityForm {
       name: ccommunity.name.to_owned(),
@@ -90,8 +91,8 @@ fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> {
       updated: None,
       actor_id: make_apub_endpoint(EndpointType::Community, &ccommunity.name).to_string(),
       local: ccommunity.local,
-      private_key: Some(community_private_key),
-      public_key: Some(community_public_key),
+      private_key: Some(keypair.private_key),
+      public_key: Some(keypair.public_key),
       last_refreshed_at: Some(naive_now()),
       published: None,
     };
index ca2fc120a44bcf979f1b8639dd20c59232fc10cd..7a706557bcc17f66e9cceac0defd0b72be4b64e9 100644 (file)
@@ -1,4 +1,5 @@
 use super::*;
+use crate::apub::signatures::Keypair;
 use crate::schema::{community, community_follower, community_moderator, community_user_ban};
 
 #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
@@ -95,6 +96,21 @@ impl Community {
   pub fn get_url(&self) -> String {
     format!("https://{}/c/{}", Settings::get().hostname, self.name)
   }
+
+  pub fn get_keypair(&self) -> Option<Keypair> {
+    if let Some(private) = self.private_key.to_owned() {
+      if let Some(public) = self.public_key.to_owned() {
+        Some(Keypair {
+          private_key: private,
+          public_key: public,
+        })
+      } else {
+        None
+      }
+    } else {
+      None
+    }
+  }
 }
 
 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
index 3a079f0916e31452299cee9942b84c9347c1fda4..dfce9f2cbdc65c2567611406fb65554fbc2349c5 100644 (file)
@@ -1,4 +1,5 @@
 use super::*;
+use crate::apub::signatures::Keypair;
 use crate::schema::user_;
 use crate::schema::user_::dsl::*;
 use crate::{is_email_regex, naive_now, Settings};
@@ -124,6 +125,21 @@ impl User_ {
     use crate::schema::user_::dsl::*;
     user_.filter(actor_id.eq(object_id)).first::<Self>(conn)
   }
+
+  pub fn get_keypair(&self) -> Option<Keypair> {
+    if let Some(private) = self.private_key.to_owned() {
+      if let Some(public) = self.public_key.to_owned() {
+        Some(Keypair {
+          private_key: private,
+          public_key: public,
+        })
+      } else {
+        None
+      }
+    } else {
+      None
+    }
+  }
 }
 
 #[derive(Debug, Serialize, Deserialize)]