Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

Commit

Permalink
Begin signature normalization
Browse files Browse the repository at this point in the history
  • Loading branch information
alexwennerberg committed May 4, 2020
1 parent 98110e3 commit 77834a2
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 108 deletions.
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ clap = "2.33.0"
chrono = "0.4.11"
diesel = { version = "1.4.4", features = ["sqlite", "r2d2"] }
env_logger = "0.7.1"
http-signature-normalization = "0.5.1"
lazy_static = "1.4.0"
log = "0.4.8"
rand = "0.7.3"
Expand Down
84 changes: 7 additions & 77 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,93 +1,23 @@
v0.1.0 goals: sharing with a small community

major

minor
- better tagging / notifications
- better session management?
- Change to GPL v3 license due to dependencies
NOTES

https://flak.tedunangst.com/post/ActivityPub-as-it-has-been-understood

Proper threading
- Render all DESCENDENTS
(allow for replies to multiple items...)

major
Federation / ActivityPub
retryeets
following/followers & separate timelines
text cant exist after URLs

REFACTORING --
DMs with expiration on read?

minor
Add limit of 255 characters for usernames and other limits to get urls to render

askama stuff
MIDDLEWARE

A nice big new feature would be event planning

https://github.com/seanmonstar/warp/blob/8b8c9950260ef7312e29c4af72b0753619a5ad5c/examples/tls.rs

sanitize on write to db

parse markdown?

send notes as cleartext and html? cleanup html parsing.

understand fn vs async fn in tokio

marketing tagline:

"An intentionally small, lightweight activitypub community"

NOTES--

NO DMS -- not really secure in AP
NO IMAGES -- issue described in https://flak.tedunangst.com/post/honk

mastodon doesnt implement client-to-server

outbox -?

TODO --

First, client to server protocol, then federation

Person
Note
Follow/Unfollow
Inbox
Outbox --> not really strictly necessary?

crates--
logging
json and serde json

activitypub to implement:

Person
https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person
MUST have
inbox, outbox

Note -- Represents a short written work typically less than a single paragraph in length.

Object properties: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object

used https://github.com/kaj/warp-diesel-ructe-sample
dotenv setup
file attachments

Webfinger

Cite this https://git.cypr.io/oz/autolink-rust/src/branch/master/src/lib.rs

Profiles:

Follow

AP key verification

Status == ActivityPub "Note"

authentication
https://github.com/messense/otpauth-rs

Expand Down
93 changes: 62 additions & 31 deletions src/ap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use diesel::sqlite::SqliteConnection;
use serde_json::json;
use serde_json::Value;
use std::env;
use std::collections::BTreeMap;
use reqwest::Request;
use chrono::Duration;
use http_signature_normalization::Config;

/// Users don't follow users in Gourami. Instead the server does hte following
/// There are a number of reasons for this:
/// Gives it a more 'community' feel -- everyone shares the same timeline
Expand All @@ -25,32 +30,37 @@ fn send_to_outbox(activity: bool) {
// activitystreams object fetch/store from db. db objects need to serialize/deserialize this object if get -> fetch from db if post -> put to db, send to inbox of followers send to inbox of followers
}

fn verify_incoming_message() {
}

enum Action {
CreateNote,
DoNothing,
// DeleteNote
}

/// get the server user json
fn server_actor() -> Value {
let domain = env::var("GOURAMI_DOMAIN").unwrap();
let actor = format!("{}/actor", domain);
let inbox = format!("{}/inbox", domain);
let public_key = fs::read_to_string(env::var("SIGNATURE_PUBKEY").unwrap()).unwrap();
fn server_actor_json() -> Value {
// TODO figure out how to get lazy static working
let DOMAIN: &str = &env::var("GOURAMI_DOMAIN").unwrap();
let SERVER_ACTOR: &str = &format!("{}/actor", &env::var("GOURAMI_DOMAIN").unwrap());
let SERVER_INBOX: &str = &format!("{}/inbox", &env::var("GOURAMI_DOMAIN").unwrap());
let SERVER_KEY_ID: &str = &format!("{}/inbox", &env::var("GOURAMI_DOMAIN").unwrap());
let SERVER_PUBLIC_KEY: &str = &fs::read_to_string(env::var("SIGNATURE_PUBKEY").unwrap()).unwrap();
json!({
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],

"id": actor,
"id": SERVER_ACTOR,
"type": "Organization", // application?
"preferredUsername": domain, // think about it
"inbox": inbox,
"preferredUsername": DOMAIN, // think about it
"inbox": SERVER_INBOX,
"publicKey": {
"id": format!("{}#main-key", actor),
"owner": actor,
"publicKeyPem": public_key
"id": SERVER_KEY_ID,
"owner": SERVER_ACTOR,
"publicKeyPem": SERVER_PUBLIC_KEY
}})
}

Expand Down Expand Up @@ -164,7 +174,6 @@ fn generate_server_follow(remote_url: String) -> Value {
/// Generate an AP create message from a new note
pub fn new_note_to_ap_message(note: &NoteInput, user: &User) -> Value {
// we need note, user. note noteinput but note obj
let conn = &POOL.get().unwrap();
// Do a bunch of db queries to get the info I need
json!({
"@context": "https://www.w3.org/ns/activitystreams",
Expand All @@ -180,7 +189,7 @@ pub fn new_note_to_ap_message(note: &NoteInput, user: &User) -> Value {
"type": "note",
"url": "abc",
"inReplyTo": "none",
"attributedTo": "joe",
"attributedTo": user.username,
"content": note.content
}
})
Expand All @@ -189,18 +198,57 @@ pub fn new_note_to_ap_message(note: &NoteInput, user: &User) -> Value {
// /// used to send to others
// fn generate_ap(activity: Activity) {
// }
pub trait HttpSignature {
fn http_sign_outgoing(self) -> Result<reqwest::Request, Box<dyn std::error::Error>>;
}

impl HttpSignature for reqwest::RequestBuilder {
fn http_sign_outgoing(self) -> Result<reqwest::Request, Box<dyn std::error::Error>> {
let req = self.build().unwrap();
let config = Config::default().set_expiration(Duration::seconds(5));
// let server_key_id =
let server_key_id: &str = &format!("{}/inbox", &env::var("GOURAMI_DOMAIN").unwrap());
let mut bt = std::collections::BTreeMap::new();
for (k, v) in req.headers().iter() {
bt.insert(k.as_str().to_owned(), v.to_str()?.to_owned());
}
let path_and_query = if let Some(query) = req.url().query() {
format!("{}?{}", req.url().path(), query)
} else {
req.url().path().to_string()
};
let unsigned = config.begin_sign(req.method().as_str(), &path_and_query, bt)?;
let sig_header = unsigned.sign(server_key_id.to_owned(), |signing_string| {
// sign here
Ok(signing_string.to_owned()) as Result<_, Box<dyn std::error::Error>>
})?
.signature_header();
println!("{:?}", sig_header);
Ok(req)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_sign_outgoing_msg() {
let body: Value = serde_json::from_str(r#"{"foo": "bar"}"#).unwrap();
let req = reqwest::Client::new()
.post("https://localhost:3030")
.json(&body)
.http_sign_outgoing();
}

#[test]
fn test_empty_string() {
// to write
}

// #[test]
fn test_mastodon_create_status_example() {
let create_note_mastodon = serde_json::from_str(r#"{
let create_note_mastodon: Value = serde_json::from_str(r#"{
"id": "https://mastodon.social/users/alexwennerberg/statuses/104028309437021899/activity",
"type": "Create",
"actor": "https://mastodon.social/users/alexwennerberg",
Expand Down Expand Up @@ -247,22 +295,5 @@ mod tests {
}
}
}"#).unwrap();
assert_eq!(
process_create_note(create_note_mastodon).unwrap(),
RemoteNoteInput {
content: String::from("hello world"),
in_reply_to: None,
neighborhood: true,
is_remote: true,
user_id: -1, // for remote. placeholder. not sure what to do with this ultimately
remote_creator: String::from("https://mastodon.social/users/alexwennerberg"),
remote_id: String::from(
"https://mastodon.social/users/alexwennerberg/statuses/104028309437021899"
),
remote_url: String::from(
"https://mastodon.social/@alexwennerberg/104028309437021899"
),
}
)
}
}

0 comments on commit 77834a2

Please sign in to comment.