[fractal/fractal-next] verification: Add timeout



commit 15f2be9122132e612fcf7ccaa1f3c36fb31547e4
Author: Julian Sparber <julian sparber net>
Date:   Sun Dec 26 17:03:43 2021 +0100

    verification: Add timeout

 src/session/verification/identity_verification.rs | 117 ++++++++++++++++++++--
 src/session/verification/verification_list.rs     |  82 +++++++++++----
 2 files changed, 167 insertions(+), 32 deletions(-)
---
diff --git a/src/session/verification/identity_verification.rs 
b/src/session/verification/identity_verification.rs
index c39b3072..4fbadc16 100644
--- a/src/session/verification/identity_verification.rs
+++ b/src/session/verification/identity_verification.rs
@@ -1,3 +1,4 @@
+use super::{VERIFICATION_CREATION_TIMEOUT, VERIFICATION_RECEIVE_TIMEOUT};
 use crate::session::user::UserExt;
 use crate::session::Session;
 use crate::session::User;
@@ -23,6 +24,7 @@ use matrix_sdk::{
     Client,
 };
 use qrcode::QrCode;
+use std::time::Duration;
 use tokio::sync::mpsc;
 
 #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::GEnum)]
@@ -118,6 +120,8 @@ mod imp {
         pub qr_code: OnceCell<QrCode>,
         pub cancel_info: OnceCell<CancelInfo>,
         pub flow_id: OnceCell<String>,
+        pub start_time: OnceCell<glib::DateTime>,
+        pub receive_time: OnceCell<glib::DateTime>,
     }
 
     #[glib::object_subclass]
@@ -175,6 +179,20 @@ mod imp {
                         None,
                         glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
                     ),
+                    glib::ParamSpec::new_boxed(
+                        "start-time",
+                        "Start Time",
+                        "The time when this verification request was started",
+                        glib::DateTime::static_type(),
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+                    ),
+                    glib::ParamSpec::new_boxed(
+                        "receive-time",
+                        "Receive Time",
+                        "The time when this verification request was received",
+                        glib::DateTime::static_type(),
+                        glib::ParamFlags::READABLE,
+                    ),
                 ]
             });
 
@@ -192,6 +210,7 @@ mod imp {
                 "user" => obj.set_user(value.get().unwrap()),
                 "session" => obj.set_session(value.get().unwrap()),
                 "flow-id" => obj.set_flow_id(value.get().unwrap()),
+                "start-time" => obj.set_start_time(value.get().unwrap()),
                 _ => unimplemented!(),
             }
         }
@@ -204,6 +223,8 @@ mod imp {
                 "display-name" => obj.display_name().to_value(),
                 "flow-id" => obj.flow_id().to_value(),
                 "supported-methods" => obj.supported_methods().to_value(),
+                "start-time" => obj.start_time().to_value(),
+                "receive-time" => obj.receive_time().to_value(),
                 _ => unimplemented!(),
             }
         }
@@ -241,6 +262,11 @@ mod imp {
                     }),
                 );
             }
+
+            self.receive_time
+                .set(glib::DateTime::new_now_local().unwrap())
+                .unwrap();
+            obj.setup_timeout();
         }
 
         fn dispose(&self, obj: &Self::Type) {
@@ -254,15 +280,30 @@ glib::wrapper! {
 }
 
 impl IdentityVerification {
-    fn for_mode(mode: Mode, session: &Session, user: &User) -> Self {
-        glib::Object::new(&[("mode", &mode), ("session", session), ("user", user)])
-            .expect("Failed to create IdentityVerification")
+    fn for_mode(mode: Mode, session: &Session, user: &User, start_time: &glib::DateTime) -> Self {
+        glib::Object::new(&[
+            ("mode", &mode),
+            ("session", session),
+            ("user", user),
+            ("start-time", start_time),
+        ])
+        .expect("Failed to create IdentityVerification")
     }
 
     /// Create a new object tracking an already existing verification request
-    pub fn for_flow_id(flow_id: &str, session: &Session, user: &User) -> Self {
-        glib::Object::new(&[("flow-id", &flow_id), ("session", session), ("user", user)])
-            .expect("Failed to create IdentityVerification")
+    pub fn for_flow_id(
+        flow_id: &str,
+        session: &Session,
+        user: &User,
+        start_time: &glib::DateTime,
+    ) -> Self {
+        glib::Object::new(&[
+            ("flow-id", &flow_id),
+            ("session", session),
+            ("user", user),
+            ("start-time", start_time),
+        ])
+        .expect("Failed to create IdentityVerification")
     }
 
     /// Creates and send a new verificaiton request
@@ -281,7 +322,12 @@ impl IdentityVerification {
 
             match handle.await.unwrap() {
                 Ok(request) => {
-                    let obj = Self::for_flow_id(request.flow_id(), session, user);
+                    let obj = Self::for_flow_id(
+                        request.flow_id(),
+                        session,
+                        user,
+                        &glib::DateTime::new_now_local().unwrap(),
+                    );
                     // This will start the request handling
                     obj.accept();
                     return obj;
@@ -294,7 +340,12 @@ impl IdentityVerification {
             error!("Starting a verification failed: Crypto identity wasn't found");
         }
 
-        Self::for_mode(Mode::Error, session, user)
+        Self::for_mode(
+            Mode::Error,
+            session,
+            user,
+            &glib::DateTime::new_now_local().unwrap(),
+        )
     }
 
     /// Accept an incomming request
@@ -315,8 +366,6 @@ impl IdentityVerification {
         let (sync_sender, sync_receiver) = mpsc::channel(100);
         priv_.sync_sender.replace(Some(sync_sender));
 
-        // TODO add timeout
-
         let handle = spawn_tokio!(async move {
             if let Some(context) =
                 Context::new(client, &user_id, &flow_id, main_sender, sync_receiver).await
@@ -367,6 +416,54 @@ impl IdentityVerification {
         priv_.session.set(session.downgrade()).unwrap()
     }
 
+    fn setup_timeout(&self) {
+        let difference = glib::DateTime::new_now_local()
+            .unwrap()
+            .difference(self.start_time());
+
+        if difference < 0 {
+            warn!("The verification request was sent in the future.");
+            self.cancel();
+            return;
+        }
+        let difference = Duration::from_secs(difference as u64);
+        let remaining_creation = VERIFICATION_CREATION_TIMEOUT.saturating_sub(difference);
+
+        let remaining_receive = VERIFICATION_RECEIVE_TIMEOUT.saturating_sub(difference);
+
+        let remaining = std::cmp::max(remaining_creation, remaining_receive);
+
+        if remaining.is_zero() {
+            self.cancel();
+            return;
+        }
+
+        glib::source::timeout_add_local(
+            remaining,
+            clone!(@weak self as obj => @default-return glib::Continue(false), move || {
+                obj.cancel();
+
+                glib::Continue(false)
+            }),
+        );
+    }
+
+    /// The time and date when this verification request was started.
+    pub fn start_time(&self) -> &glib::DateTime {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        priv_.start_time.get().unwrap()
+    }
+
+    fn set_start_time(&self, time: glib::DateTime) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        priv_.start_time.set(time).unwrap();
+    }
+
+    pub fn receive_time(&self) -> &glib::DateTime {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        priv_.receive_time.get().unwrap()
+    }
+
     fn supported_methods(&self) -> SupportedMethods {
         let priv_ = imp::IdentityVerification::from_instance(self);
         priv_.supported_methods.get()
diff --git a/src/session/verification/verification_list.rs b/src/session/verification/verification_list.rs
index 5b3290ba..8363fb4d 100644
--- a/src/session/verification/verification_list.rs
+++ b/src/session/verification/verification_list.rs
@@ -1,11 +1,14 @@
 use crate::session::user::UserExt;
+use crate::session::{
+    verification::{IdentityVerification, VERIFICATION_CREATION_TIMEOUT},
+    Session,
+};
 use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
+use log::warn;
 use matrix_sdk::ruma::{
     api::client::r0::sync::sync_events::ToDevice, events::AnyToDeviceEvent, identifiers::UserId,
 };
 
-use crate::session::{verification::IdentityVerification, Session};
-
 #[derive(Hash, PartialEq, Eq, Debug)]
 pub struct FlowId {
     user_id: UserId,
@@ -112,46 +115,81 @@ impl VerificationList {
 
     pub fn handle_response_to_device(&self, to_device: ToDevice) {
         for event in to_device.events.iter().filter_map(|e| e.deserialize().ok()) {
-            let flow_id = match event {
+            let request = match event {
                 AnyToDeviceEvent::KeyVerificationRequest(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    let flow_id = FlowId::new(e.sender, e.content.transaction_id);
+                    if let Some(request) = self.get_by_id(&flow_id) {
+                        Some(request)
+                    } else {
+                        let session = self.session();
+                        let user = session.user().unwrap();
+                        // ToDevice verifications can only be send by us
+                        if &flow_id.user_id != user.user_id() {
+                            continue;
+                        }
+
+                        // Ignore request that are too old
+                        let start_time = if let Some(time) = e.content.timestamp.to_system_time() {
+                            if let Ok(duration) = time.elapsed() {
+                                if duration > VERIFICATION_CREATION_TIMEOUT {
+                                    continue;
+                                }
+
+                                if let Ok(time) = glib::DateTime::from_unix_utc(
+                                    e.content.timestamp.as_secs().into(),
+                                )
+                                .and_then(|t| t.to_local())
+                                {
+                                    time
+                                } else {
+                                    warn!("Ignor verification request because getting a correct timestamp 
failed");
+                                    continue;
+                                }
+                            } else {
+                                warn!("Ignoring verification request because it was sent in the future. The 
system time of the server or the local machine is probably wrong.");
+                                continue;
+                            }
+                        } else {
+                            continue;
+                        };
+
+                        let request = IdentityVerification::for_flow_id(
+                            &flow_id.flow_id,
+                            &session,
+                            user,
+                            &start_time,
+                        );
+                        self.add(request.clone());
+                        Some(request)
+                    }
                 }
                 AnyToDeviceEvent::KeyVerificationReady(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 AnyToDeviceEvent::KeyVerificationStart(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 AnyToDeviceEvent::KeyVerificationCancel(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 AnyToDeviceEvent::KeyVerificationAccept(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 AnyToDeviceEvent::KeyVerificationMac(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 AnyToDeviceEvent::KeyVerificationKey(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 AnyToDeviceEvent::KeyVerificationDone(e) => {
-                    FlowId::new(e.sender, e.content.transaction_id)
+                    self.get_by_id(&FlowId::new(e.sender, e.content.transaction_id))
                 }
                 _ => continue,
             };
-
-            if let Some(request) = self.get_by_id(&flow_id) {
+            if let Some(request) = request {
                 request.notify_state();
             } else {
-                let session = self.session();
-                let user = session.user().unwrap();
-                // ToDevice verifications can only be send by us
-                if &flow_id.user_id == user.user_id() {
-                    let request =
-                        IdentityVerification::for_flow_id(&flow_id.flow_id, &session, user);
-                    request.notify_state();
-                    self.add(request);
-                }
+                warn!("Recevied verification event, but we don't have the inital event.");
             }
         }
     }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]