Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(): validated oob invitation #287

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Christiantyemele marked this conversation as resolved.
Show resolved Hide resolved

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);
Christiantyemele marked this conversation as resolved.
Show resolved Hide resolved
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)
Christiantyemele marked this conversation as resolved.
Show resolved Hide resolved
.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))
Christiantyemele marked this conversation as resolved.
Show resolved Hide resolved
.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
Loading