mirror of
https://github.com/chatmail/core.git
synced 2026-05-09 01:46:30 +03:00
Add happy path test for secure-join
This commit is contained in:
@@ -1166,7 +1166,7 @@ pub async fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<ChatId
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a normal chat with a single user. To create group chats,
|
/// Create a normal chat with a single user. To create group chats,
|
||||||
/// see dc_create_group_chat().
|
/// see [Chat::create_group_chat].
|
||||||
///
|
///
|
||||||
/// If a chat already exists, this ID is returned, otherwise a new chat is created;
|
/// If a chat already exists, this ID is returned, otherwise a new chat is created;
|
||||||
/// this new chat may already contain messages, eg. from the deaddrop, to get the
|
/// this new chat may already contain messages, eg. from the deaddrop, to get the
|
||||||
|
|||||||
@@ -1093,6 +1093,7 @@ fn encrypted_and_signed(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::chat;
|
||||||
use crate::peerstate::Peerstate;
|
use crate::peerstate::Peerstate;
|
||||||
use crate::test_utils::TestContext;
|
use crate::test_utils::TestContext;
|
||||||
|
|
||||||
@@ -1306,4 +1307,111 @@ mod tests {
|
|||||||
"vc-contact-confirm-received"
|
"vc-contact-confirm-received"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_secure_join() {
|
||||||
|
let alice = TestContext::new_alice().await;
|
||||||
|
let bob = TestContext::new_bob().await;
|
||||||
|
|
||||||
|
let chatid = chat::create_group_chat(&alice.ctx, VerifiedStatus::Verified, "the chat")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Generate QR-code, secure-join implied by chatid
|
||||||
|
let qr = dc_get_securejoin_qr(&alice.ctx, chatid).await.unwrap();
|
||||||
|
|
||||||
|
// Bob scans QR-code, sends vg-request; blocks on ongoing process
|
||||||
|
let joiner = {
|
||||||
|
let qr = qr.clone();
|
||||||
|
let ctx = bob.ctx.clone();
|
||||||
|
async_std::task::spawn(async move { dc_join_securejoin(&ctx, &qr).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let sent = bob.pop_sent_msg().await;
|
||||||
|
assert_eq!(sent.recipient(), "alice@example.com".parse().unwrap());
|
||||||
|
let msg = alice.parse_msg(&sent).await;
|
||||||
|
assert!(!msg.was_encrypted());
|
||||||
|
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-request");
|
||||||
|
assert!(msg.get(HeaderDef::SecureJoinInvitenumber).is_some());
|
||||||
|
|
||||||
|
// Alice receives vg-request, sends vg-auth-required
|
||||||
|
alice.recv_msg(&sent).await;
|
||||||
|
|
||||||
|
let sent = alice.pop_sent_msg().await;
|
||||||
|
let msg = bob.parse_msg(&sent).await;
|
||||||
|
assert!(msg.was_encrypted());
|
||||||
|
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-auth-required");
|
||||||
|
|
||||||
|
// Bob receives vg-auth-required, sends vg-request-with-auth
|
||||||
|
bob.recv_msg(&sent).await;
|
||||||
|
let sent = bob.pop_sent_msg().await;
|
||||||
|
let msg = alice.parse_msg(&sent).await;
|
||||||
|
assert!(msg.was_encrypted());
|
||||||
|
assert_eq!(
|
||||||
|
msg.get(HeaderDef::SecureJoin).unwrap(),
|
||||||
|
"vg-request-with-auth"
|
||||||
|
);
|
||||||
|
assert!(msg.get(HeaderDef::SecureJoinAuth).is_some());
|
||||||
|
let bob_fp = SignedPublicKey::load_self(&bob.ctx)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.fingerprint();
|
||||||
|
assert_eq!(
|
||||||
|
*msg.get(HeaderDef::SecureJoinFingerprint).unwrap(),
|
||||||
|
bob_fp.hex()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice should not yet have Bob verified
|
||||||
|
let contact_bob_id =
|
||||||
|
Contact::lookup_id_by_addr(&alice.ctx, "bob@example.net", Origin::Unknown).await;
|
||||||
|
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
contact_bob.is_verified(&alice.ctx).await,
|
||||||
|
VerifiedStatus::Unverified
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice receives vg-request-with-auth, sends vg-member-added
|
||||||
|
alice.recv_msg(&sent).await;
|
||||||
|
assert_eq!(
|
||||||
|
contact_bob.is_verified(&alice.ctx).await,
|
||||||
|
VerifiedStatus::BidirectVerified
|
||||||
|
);
|
||||||
|
|
||||||
|
let sent = alice.pop_sent_msg().await;
|
||||||
|
let msg = bob.parse_msg(&sent).await;
|
||||||
|
assert!(msg.was_encrypted());
|
||||||
|
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-member-added");
|
||||||
|
|
||||||
|
// Bob should not yet have Alice verified
|
||||||
|
let contact_alice_id =
|
||||||
|
Contact::lookup_id_by_addr(&bob.ctx, "alice@example.com", Origin::Unknown).await;
|
||||||
|
let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
contact_bob.is_verified(&bob.ctx).await,
|
||||||
|
VerifiedStatus::Unverified
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bob receives vg-member-added, sends vg-member-added-received
|
||||||
|
bob.recv_msg(&sent).await;
|
||||||
|
assert_eq!(
|
||||||
|
contact_alice.is_verified(&bob.ctx).await,
|
||||||
|
VerifiedStatus::BidirectVerified
|
||||||
|
);
|
||||||
|
|
||||||
|
let sent = bob.pop_sent_msg().await;
|
||||||
|
let msg = alice.parse_msg(&sent).await;
|
||||||
|
assert!(msg.was_encrypted());
|
||||||
|
assert_eq!(
|
||||||
|
msg.get(HeaderDef::SecureJoin).unwrap(),
|
||||||
|
"vg-member-added-received"
|
||||||
|
);
|
||||||
|
|
||||||
|
let bob_chatid = joiner.await;
|
||||||
|
let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await.unwrap();
|
||||||
|
assert!(bob_chat.is_verified());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
//!
|
//!
|
||||||
//! This module is only compiled for test runs.
|
//! This module is only compiled for test runs.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use async_std::path::PathBuf;
|
use async_std::path::PathBuf;
|
||||||
|
use async_std::sync::RwLock;
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
use crate::chat::ChatId;
|
use crate::chat::ChatId;
|
||||||
@@ -22,11 +23,12 @@ use crate::param::{Param, Params};
|
|||||||
///
|
///
|
||||||
/// The temporary directory can be used to store the SQLite database,
|
/// The temporary directory can be used to store the SQLite database,
|
||||||
/// see e.g. [test_context] which does this.
|
/// see e.g. [test_context] which does this.
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct TestContext {
|
pub(crate) struct TestContext {
|
||||||
pub ctx: Context,
|
pub ctx: Context,
|
||||||
pub dir: TempDir,
|
pub dir: TempDir,
|
||||||
/// Counter for fake IMAP UIDs in [recv_msg], for private use in that function only.
|
/// Counter for fake IMAP UIDs in [recv_msg], for private use in that function only.
|
||||||
recv_idx: RefCell<u32>,
|
recv_idx: RwLock<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestContext {
|
impl TestContext {
|
||||||
@@ -49,7 +51,7 @@ impl TestContext {
|
|||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
dir,
|
dir,
|
||||||
recv_idx: RefCell::new(0),
|
recv_idx: RwLock::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,26 +114,36 @@ impl TestContext {
|
|||||||
///
|
///
|
||||||
/// Panics if there is no message or on any error.
|
/// Panics if there is no message or on any error.
|
||||||
pub async fn pop_sent_msg(&self) -> SentMessage {
|
pub async fn pop_sent_msg(&self) -> SentMessage {
|
||||||
let (rowid, foreign_id, raw_params) = self
|
let start = Instant::now();
|
||||||
.ctx
|
let (rowid, foreign_id, raw_params) = loop {
|
||||||
.sql
|
let row = self
|
||||||
.query_row(
|
.ctx
|
||||||
r#"
|
.sql
|
||||||
|
.query_row(
|
||||||
|
r#"
|
||||||
SELECT id, foreign_id, param
|
SELECT id, foreign_id, param
|
||||||
FROM jobs
|
FROM jobs
|
||||||
WHERE action=?
|
WHERE action=?
|
||||||
ORDER BY desired_timestamp;
|
ORDER BY desired_timestamp;
|
||||||
"#,
|
"#,
|
||||||
paramsv![Action::SendMsgToSmtp],
|
paramsv![Action::SendMsgToSmtp],
|
||||||
|row| {
|
|row| {
|
||||||
let id: i64 = row.get(0)?;
|
let id: i64 = row.get(0)?;
|
||||||
let foreign_id: i64 = row.get(1)?;
|
let foreign_id: i64 = row.get(1)?;
|
||||||
let param: String = row.get(2)?;
|
let param: String = row.get(2)?;
|
||||||
Ok((id, foreign_id, param))
|
Ok((id, foreign_id, param))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await;
|
||||||
.expect("no sent message found in jobs table");
|
if let Ok(row) = row {
|
||||||
|
break row;
|
||||||
|
}
|
||||||
|
if start.elapsed() < Duration::from_secs(3) {
|
||||||
|
async_std::task::sleep(Duration::from_millis(100)).await;
|
||||||
|
} else {
|
||||||
|
panic!("no sent message found in jobs table");
|
||||||
|
}
|
||||||
|
};
|
||||||
let id = ChatId::new(foreign_id as u32);
|
let id = ChatId::new(foreign_id as u32);
|
||||||
let params = Params::from_str(&raw_params).unwrap();
|
let params = Params::from_str(&raw_params).unwrap();
|
||||||
let blob_path = params
|
let blob_path = params
|
||||||
@@ -169,7 +181,7 @@ impl TestContext {
|
|||||||
///
|
///
|
||||||
/// Receives a message using the `dc_receive_imf()` pipeline.
|
/// Receives a message using the `dc_receive_imf()` pipeline.
|
||||||
pub async fn recv_msg(&self, msg: &SentMessage) {
|
pub async fn recv_msg(&self, msg: &SentMessage) {
|
||||||
let mut idx = self.recv_idx.borrow_mut();
|
let mut idx = self.recv_idx.write().await;
|
||||||
*idx += 1;
|
*idx += 1;
|
||||||
dc_receive_imf(&self.ctx, msg.payload().as_bytes(), "INBOX", *idx, false)
|
dc_receive_imf(&self.ctx, msg.payload().as_bytes(), "INBOX", *idx, false)
|
||||||
.await
|
.await
|
||||||
|
|||||||
Reference in New Issue
Block a user