Skip to content

Commit

Permalink
feat(): validated oob invitation
Browse files Browse the repository at this point in the history
  • Loading branch information
Christiantyemele committed Dec 18, 2024
1 parent 7acf26a commit 1809705
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 109 deletions.
53 changes: 37 additions & 16 deletions crates/web-plugins/oob-messages/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ use std::io::Result as IoResult;
// ```

#[derive(Debug, Serialize, Deserialize, Clone)]
struct OobMessage {
pub(crate) struct OobMessage {
#[serde(rename = "type")]
oob_type: String,
id: String,
from: String,
body: Body,
}
type Ooburl = String;

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename = "use")]
Expand All @@ -55,7 +56,11 @@ struct Body {
label: String,
accept: Vec<String>,
}

impl Default for OobMessage {
fn default() -> Self {
OobMessage::new("")
}
}
impl OobMessage {
fn new(did: &str) -> Self {
let id = uuid::Uuid::new_v4().to_string();
Expand All @@ -74,20 +79,21 @@ impl OobMessage {
}
}

fn serialize_oob_message(oob_message: &OobMessage, url: &str) -> Result<String, String> {
fn serialize_oob_message(oob_message: &OobMessage, url: &str) -> Result<Ooburl, String> {
let plaintext =
to_string(oob_message).map_err(|e| format!("Serialization error: {}", e))?;
let encoded_jwm = Base64Url.encode(plaintext.as_bytes());

Ok(format!("{}?_oob={}", url, encoded_jwm))
let ooburl = format!("{}/_oob{}", url, encoded_jwm);

Ok(ooburl)
}
}

// Receives server path/port and local storage path and returns a String with the OOB URL.
pub(crate) fn retrieve_or_generate_oob_inv<F>(
fs: &mut F,
server_public_domain: &str,
server_local_port: &str,
storage_dirpath: &str,
) -> Result<String, String>
where
Expand All @@ -112,7 +118,7 @@ where

let did = diddoc.id.clone();
let oob_message = OobMessage::new(&did);
let url: &String = &format!("{}:{}", server_public_domain, server_local_port);
let url: &String = &format!("http://{}", server_public_domain);
let oob_url = OobMessage::serialize_oob_message(&oob_message, url)
.map_err(|err| format!("Serialization error: {err}"))?;

Expand Down Expand Up @@ -205,8 +211,10 @@ use std::path::Path;

#[cfg(test)]
mod tests {

use super::*;
use mockall::{predicate::*, *};
use multibase::Base::Base64Url;

mock! {
pub FileSystem {}
Expand All @@ -218,6 +226,15 @@ mod tests {
fn write_with_lock(&self, path: &Path, content: &str) -> IoResult<()>;
}
}
fn test_serialize_oobmessage() -> String {
let did = "did:key:z6MkfyTREjTxQ8hUwSwBPeDHf3uPL3qCjSSuNPwsyMpWUGH7#z6LSbuUXWSgPfpiDBjUK6E7yiCKMN2eKJsXn5b55ZgqGz6Mr";
let oobmessage = OobMessage::new(did);
let server_public_domain = "https://example.com";

let url = format!("{}", server_public_domain);
let encoded_oob = OobMessage::serialize_oob_message(&oobmessage, &url).unwrap();
encoded_oob
}

#[test]
fn test_create_oob_message() {
Expand Down Expand Up @@ -249,15 +266,23 @@ mod tests {
.unwrap_or_else(|err| panic!("Failed to serialize oob message: {}", err));

assert!(!oob_url.is_empty());
assert!(oob_url.starts_with(&format!("{}?_oob=", url)));
assert!(oob_url.contains("_oob="));
assert!(oob_url.starts_with(&format!("{}/_oob", url)));
assert!(oob_url.contains("_oob"));
}
#[test]
fn test_deserialize_oob_message() {
let did = "did:key:z6MkfyTREjTxQ8hUwSwBPeDHf3uPL3qCjSSuNPwsyMpWUGH7#z6LSbuUXWSgPfpiDBjUK6E7yiCKMN2eKJsXn5b55ZgqGz6Mr";
let serialized_msg = test_serialize_oobmessage();
let oobmessage: Vec<&str> = serialized_msg.split("/_oob").collect();
let oobmessage = oobmessage.get(1).unwrap().to_string();
let decode_msg = Base64Url.decode(oobmessage).unwrap();
let oobmessage: OobMessage = serde_json::from_slice(&decode_msg).unwrap();
assert_eq!(oobmessage.from, did);
}

#[test]
fn test_retrieve_or_generate_oob_inv() {
// Test data
let server_public_domain = "https://example.com";
let server_local_port = "8080";
let storage_dirpath = "testpath";

let mut mock_fs = MockFileSystem::new();
Expand Down Expand Up @@ -295,12 +320,8 @@ mod tests {
.withf(|path, _content| path == Path::new("testpath/oob_invitation.txt"))
.returning(|_, _| Ok(()));

let result = retrieve_or_generate_oob_inv(
&mut mock_fs,
server_public_domain,
server_local_port,
storage_dirpath,
);
let result =
retrieve_or_generate_oob_inv(&mut mock_fs, server_public_domain, storage_dirpath);

assert!(result.is_ok());
}
Expand Down
29 changes: 12 additions & 17 deletions crates/web-plugins/oob-messages/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ pub struct OOBMessages {
struct OOBMessagesEnv {
storage_dirpath: String,
server_public_domain: String,
server_local_port: String,
}

#[derive(Clone)]
pub(crate) struct OOBMessagesState {
pub(crate) filesystem: Arc<Mutex<dyn FileSystem>>,
pub(crate) oobmessage: String,
}

fn get_env() -> Result<OOBMessagesEnv, PluginError> {
Expand All @@ -33,14 +33,9 @@ fn get_env() -> Result<OOBMessagesEnv, PluginError> {
PluginError::InitError("SERVER_PUBLIC_DOMAIN env variable required".to_owned())
})?;

let server_local_port = std::env::var("SERVER_LOCAL_PORT").map_err(|_| {
PluginError::InitError("SERVER_LOCAL_PORT env variable required".to_owned())
})?;

Ok(OOBMessagesEnv {
storage_dirpath,
server_public_domain,
server_local_port,
})
}

Expand All @@ -53,17 +48,13 @@ impl Plugin for OOBMessages {
let env = get_env()?;
let mut fs = StdFileSystem;

let oob_inv = retrieve_or_generate_oob_inv(
&mut fs,
&env.server_public_domain,
&env.server_local_port,
&env.storage_dirpath,
)
.map_err(|err| {
PluginError::InitError(format!(
"Error retrieving or generating OOB invitation: {err}"
))
})?;
let oob_inv =
retrieve_or_generate_oob_inv(&mut fs, &env.server_public_domain, &env.storage_dirpath)
.map_err(|err| {
PluginError::InitError(format!(
"Error retrieving or generating OOB invitation: {err}"
))
})?;

tracing::debug!("Out Of Band Invitation: {}", oob_inv);

Expand All @@ -74,8 +65,12 @@ impl Plugin for OOBMessages {
})?;

self.env = Some(env);
let oobmessage: Vec<&str> = oob_inv.split("/_").collect();
let oobmessage = oobmessage.get(1).unwrap_or(&"").to_string();

self.state = Some(OOBMessagesState {
filesystem: Arc::new(Mutex::new(fs)),
oobmessage,
});

Ok(())
Expand Down
14 changes: 12 additions & 2 deletions crates/web-plugins/oob-messages/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ use crate::{
web::handler::{handler_landing_page_oob, handler_oob_inv, handler_oob_qr},
};
use axum::{routing::get, Router};
use handler::decode_oob_inv;
use std::sync::Arc;

pub(crate) fn routes(state: Arc<OOBMessagesState>) -> Router {
let invitation_path = format!("/_{}", &state.oobmessage);
Router::new() //
.route("/oob_url", get(handler_oob_inv))
.route("/oob_qr", get(handler_oob_qr))
.route("/", get(handler_landing_page_oob))
// handle oob invitation to invitation message
.route(&invitation_path, get(decode_oob_inv))
.with_state(state)
}

#[cfg(test)]
mod tests {
use crate::models::retrieve_or_generate_oob_inv;

use super::*;
use axum::{
body::Body,
Expand All @@ -30,10 +36,14 @@ mod tests {
std::env::set_var("STORAGE_DIRPATH", "tmp");
std::env::set_var("SERVER_PUBLIC_DOMAIN", "example.com");
std::env::set_var("SERVER_LOCAL_PORT", "8080");

let fs = MockFileSystem;
let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").unwrap();
let storage_dirpath = std::env::var("STORAGE_DIRPATH").unwrap();
let mut fs = MockFileSystem;
let oobmessage =
retrieve_or_generate_oob_inv(&mut fs, &server_public_domain, &storage_dirpath).unwrap();
let state = Arc::new(OOBMessagesState {
filesystem: Arc::new(Mutex::new(fs)),
oobmessage,
});
let app = routes(state.clone());

Expand Down
Loading

0 comments on commit 1809705

Please sign in to comment.