Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 3 additions & 4 deletions src/qr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,19 +766,18 @@ pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<()> {
authcode,
..
} => {
token::delete(context, token::Namespace::InviteNumber, &invitenumber).await?;
token::delete(context, token::Namespace::Auth, &authcode).await?;
token::delete(context, "").await?;
context
.sync_qr_code_token_deletion(invitenumber, authcode)
.await?;
}
Qr::WithdrawVerifyGroup {
grpid,
invitenumber,
authcode,
..
} => {
token::delete(context, token::Namespace::InviteNumber, &invitenumber).await?;
token::delete(context, token::Namespace::Auth, &authcode).await?;
token::delete(context, &grpid).await?;
context
.sync_qr_code_token_deletion(invitenumber, authcode)
.await?;
Expand Down
73 changes: 72 additions & 1 deletion src/qr/qr_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;
use crate::chat::{ProtectionStatus, create_group_chat};
use crate::config::Config;
use crate::securejoin::get_securejoin_qr;
use crate::test_utils::{TestContext, TestContextManager};
use crate::test_utils::{TestContext, TestContextManager, sync};

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_http() -> Result<()> {
Expand Down Expand Up @@ -509,6 +509,77 @@ async fn test_withdraw_verifygroup() -> Result<()> {
Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_withdraw_multidevice() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let alice2 = &tcm.alice().await;

alice.set_config_bool(Config::SyncMsgs, true).await?;
alice2.set_config_bool(Config::SyncMsgs, true).await?;

// Alice creates two QR codes on the first device:
// group QR code and contact QR code.
let chat_id = create_group_chat(alice, ProtectionStatus::Unprotected, "Group").await?;
let chat2_id = create_group_chat(alice, ProtectionStatus::Unprotected, "Group 2").await?;
let contact_qr = get_securejoin_qr(alice, None).await?;
let group_qr = get_securejoin_qr(alice, Some(chat_id)).await?;
let group2_qr = get_securejoin_qr(alice, Some(chat2_id)).await?;

assert!(matches!(
check_qr(alice, &contact_qr).await?,
Qr::WithdrawVerifyContact { .. }
));
assert!(matches!(
check_qr(alice, &group_qr).await?,
Qr::WithdrawVerifyGroup { .. }
));

// Sync group QR codes.
sync(alice, alice2).await;
assert!(matches!(
check_qr(alice2, &group_qr).await?,
Qr::WithdrawVerifyGroup { .. }
));
assert!(matches!(
check_qr(alice2, &group2_qr).await?,
Qr::WithdrawVerifyGroup { .. }
));

// Alice creates a contact QR code on second device
// and withdraws it.
let contact_qr2 = get_securejoin_qr(alice2, None).await?;
set_config_from_qr(alice2, &contact_qr2).await?;
assert!(matches!(
check_qr(alice2, &contact_qr2).await?,
Qr::ReviveVerifyContact { .. }
));

// Alice also withdraws second group QR code on second device.
set_config_from_qr(alice2, &group2_qr).await?;

// Sync messages are sent from Alice's second device to first device.
sync(alice2, alice).await;

// Now first device has reset all contact QR codes
// and second group QR code,
// but first group QR code is still valid.
assert!(matches!(
check_qr(alice, &contact_qr2).await?,
Qr::ReviveVerifyContact { .. }
));
assert!(matches!(
check_qr(alice, &group_qr).await?,
Qr::WithdrawVerifyGroup { .. }
));
assert!(matches!(
check_qr(alice, &group2_qr).await?,
Qr::ReviveVerifyGroup { .. }
));

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_and_apply_dclogin() -> Result<()> {
let ctx = TestContext::new().await;
Expand Down
15 changes: 11 additions & 4 deletions src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,15 @@ impl Context {
}

async fn delete_qr_token(&self, token: &QrTokenData) -> Result<()> {
token::delete(self, Namespace::InviteNumber, &token.invitenumber).await?;
token::delete(self, Namespace::Auth, &token.auth).await?;
self.sql
.execute(
"DELETE FROM tokens
WHERE foreign_key IN
(SELECT foreign_key FROM tokens
WHERE token=? OR token=?)",
(&token.invitenumber, &token.auth),
)
.await?;
Ok(())
}

Expand Down Expand Up @@ -568,8 +575,8 @@ mod tests {
.await?
.is_none()
);
assert!(token::exists(&t, Namespace::InviteNumber, "yip-in").await?);
assert!(token::exists(&t, Namespace::Auth, "yip-auth").await?);
assert!(!token::exists(&t, Namespace::InviteNumber, "yip-in").await?);
assert!(!token::exists(&t, Namespace::Auth, "yip-auth").await?);
assert!(!token::exists(&t, Namespace::Auth, "non-existent").await?);
assert!(!token::exists(&t, Namespace::Auth, "directly deleted").await?);

Expand Down
11 changes: 6 additions & 5 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ pub async fn auth_foreign_key(context: &Context, token: &str) -> Result<Option<S
.await
}

pub async fn delete(context: &Context, namespace: Namespace, token: &str) -> Result<()> {
/// Resets all tokens corresponding to the `foreign_key`.
///
/// `foreign_key` is a group ID to reset all group tokens
/// or empty string to reset all setup contact tokens.
pub async fn delete(context: &Context, foreign_key: &str) -> Result<()> {
context
.sql
.execute(
"DELETE FROM tokens WHERE namespc=? AND token=?;",
(namespace, token),
)
.execute("DELETE FROM tokens WHERE foreign_key=?", (foreign_key,))
.await?;
Ok(())
}
Loading