Show popup when device is not targeted.

When someone is sending a pfs message to us but did not include our own
device, a pop up is shown propmting the user to connect with the user.

The reason for receiving messages that are not targeting our devices are
various:

1) The account was just recovered (which means it is a new installation
id)
2) More than 3 devices are in use (we only keep max 3 devices in sync)
3) The sender has used an old bundle which does not include the current
device

Eventually we will reduce the likelihood of this scenario happening, but
we can't dismiss it completely.

It's only enabled when PFS is enabled.

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-12-27 13:12:54 +01:00
parent ad99ed2a28
commit b56fd2e29f
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
11 changed files with 215 additions and 51 deletions

View File

@ -0,0 +1,63 @@
(ns status-im.contact-recovery.core
(:require
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]
[status-im.data-store.contact-recovery :as data-store.contact-recovery]
[status-im.utils.fx :as fx]
[status-im.accounts.db :as accounts.db]
[status-im.contact.core :as models.contact]))
(defn prompt-dismissed! [public-key]
(re-frame/dispatch [:contact-recovery.ui/prompt-dismissed public-key]))
(defn prompt-accepted! [public-key]
(re-frame/dispatch [:contact-recovery.ui/prompt-accepted public-key]))
(defn show-contact-recovery-fx
"Check that a pop up for that given user is not already shown, if not proceed fetching from the db whether we should be showing it"
[{:keys [db] :as cofx} public-key]
(let [my-public-key (accounts.db/current-public-key cofx)
pfs? (get-in db [:account/account :settings :pfs?])]
(when (and (not= public-key my-public-key)
pfs?
(not (get-in db [:contact-recovery/pop-up public-key])))
{:db (update db :contact-recovery/pop-up conj public-key)
:contact-recovery/show-contact-recovery-message public-key})))
(fx/defn prompt-dismissed [{:keys [db]} public-key]
{:db (update db :contact-recovery/pop-up disj public-key)})
(defn show-contact-recovery-message? [public-key]
(not (data-store.contact-recovery/get-contact-recovery-by-id public-key)))
(defn show-contact-recovery-message-fx [public-key]
(when (show-contact-recovery-message? public-key)
(re-frame/dispatch [:contact-recovery.callback/show-contact-recovery-message public-key])))
(re-frame/reg-fx
:contact-recovery/show-contact-recovery-message
show-contact-recovery-message-fx)
(fx/defn save-contact-recovery [{:keys [now]} public-key]
{:data-store/tx [(data-store.contact-recovery/save-contact-recovery-tx {:timestamp now
:id public-key})]})
(fx/defn prompt-accepted [cofx public-key]
(fx/merge
cofx
(prompt-dismissed public-key)
(save-contact-recovery public-key)))
(fx/defn show-contact-recovery-message [{:keys [db] :as cofx} public-key]
(let [pfs? (get-in db [:account/account :settings :pfs?])
contact (models.contact/build-contact cofx public-key)
popup {:ui/show-confirmation {:title (i18n/label :t/contact-recovery-title {:name (:name contact)})
:content (i18n/label :t/contact-recovery-content {:name (:name contact)})
:confirm-button-text (i18n/label :t/add-to-contacts)
:cancel-button-text (i18n/label :t/cancel)
:on-cancel #(prompt-dismissed! public-key)
:on-accept #(prompt-accepted! public-key)}}]
(when pfs?
(fx/merge cofx
popup))))

View File

@ -0,0 +1,14 @@
(ns status-im.data-store.contact-recovery
(:require [status-im.data-store.realm.core :as core]))
(defn get-contact-recovery-by-id [public-key]
(core/single (core/get-by-field @core/account-realm :contact-recovery :id public-key)))
(defn save-contact-recovery-tx
"Returns tx function for saving a contact-recovery"
[contact-recovery]
(fn [realm]
(core/create realm
:contact-recovery
contact-recovery
true)))

View File

@ -0,0 +1,7 @@
(ns status-im.data-store.realm.schemas.account.contact-recovery)
(def v1 {:name :contact-recovery
:primaryKey :id
:properties {:id :string
:timestamp {:type :int
:optional true}}})

View File

@ -13,6 +13,7 @@
[status-im.data-store.realm.schemas.account.request :as request]
[status-im.data-store.realm.schemas.account.membership-update :as membership-update]
[status-im.data-store.realm.schemas.account.installation :as installation]
[status-im.data-store.realm.schemas.account.contact-recovery :as contact-recovery]
[status-im.data-store.realm.schemas.account.migrations :as migrations]
[taoensso.timbre :as log]))
@ -343,6 +344,20 @@
browser/v8
dapp-permissions/v9])
(def v32 [chat/v13
transport/v7
contact/v3
message/v9
mailserver/v11
mailserver-topic/v1
user-status/v2
membership-update/v1
installation/v2
local-storage/v1
browser/v8
dapp-permissions/v9
contact-recovery/v1])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -436,4 +451,7 @@
:migration migrations/v30}
{:schema v31
:schemaVersion 31
:migration (constantly nil)}
{:schema v32
:schemaVersion 32
:migration (constantly nil)}])

View File

@ -16,6 +16,7 @@
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.message :as chat.message]
[status-im.contact.core :as contact]
[status-im.contact-recovery.core :as contact-recovery]
[status-im.data-store.core :as data-store]
[status-im.extensions.core :as extensions]
[status-im.extensions.registry :as extensions.registry]
@ -1404,3 +1405,25 @@
:pairing.callback/disable-installation-success
(fn [cofx [_ installation-id]]
(pairing/disable cofx installation-id)))
;; Contact recovery module
(handlers/register-handler-fx
:contact-recovery.ui/prompt-accepted
[(re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ public-key]]
(fx/merge
cofx
(contact/add-contact public-key)
(contact-recovery/prompt-accepted public-key))))
(handlers/register-handler-fx
:contact-recovery.ui/prompt-dismissed
(fn [cofx [_ public-key]]
(contact-recovery/prompt-dismissed cofx public-key)))
(handlers/register-handler-fx
:contact-recovery.callback/show-contact-recovery-message
[(re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ public-key]]
(contact-recovery/show-contact-recovery-message cofx public-key)))

View File

@ -4,6 +4,7 @@
[status-im.init.core :as init]
[status-im.node.core :as node]
[status-im.pairing.core :as pairing]
[status-im.contact-recovery.core :as contact-recovery]
[status-im.mailserver.core :as mailserver]
[status-im.transport.message.core :as transport.message]
[status-im.utils.fx :as fx]
@ -75,5 +76,6 @@
"mailserver.request.completed" (mailserver/handle-request-completed cofx event)
"mailserver.request.expired" (when (accounts.db/logged-in? cofx)
(mailserver/resend-request cofx {:request-id (:hash event)}))
"messages.decrypt.failed" (contact-recovery/show-contact-recovery-fx cofx (:sender event))
"discovery.summary" (summary cofx event)
(log/debug "Event " type " not handled"))))

View File

@ -35,19 +35,36 @@
:resend? resend?
:now now}))})
(defn send-public-message
"Sends the payload to topic"
[{:keys [db] :as cofx} chat-id success-event payload]
(let [{:keys [web3]} db]
{:shh/send-public-message [{:web3 web3
:success-event success-event
:src (accounts.db/current-public-key cofx)
:chat chat-id
:payload payload}]}))
(fx/defn send-with-sym-key
"Sends the payload using symetric key and topic from db (looked up by `chat-id`)"
[{:keys [db] :as cofx} {:keys [payload chat-id success-event]}]
;; we assume that the chat contains the contact public-key
(let [{:keys [web3]} db
{:keys [sym-key-id topic]} (get-in db [:transport/chats chat-id])]
{:shh/post [{:web3 web3
:success-event success-event
:message (merge {:sig (accounts.db/current-public-key cofx)
:symKeyID sym-key-id
:payload payload
:topic topic}
whisper-opts)}]}))
{:keys [sym-key-id topic]} (get-in db [:transport/chats chat-id])
pfs? (get-in db [:account/account :settings :pfs?])]
(if pfs?
(send-public-message
cofx
chat-id
success-event
payload)
{:shh/post [{:web3 web3
:success-event success-event
:message (merge {:sig (accounts.db/current-public-key cofx)
:symKeyID sym-key-id
:payload payload
:topic topic}
whisper-opts)}]})))
(fx/defn send-direct-message
"Sends the payload using to dst"
@ -59,33 +76,28 @@
:dst dst
:payload payload}]}))
(defn send-public-message
"Sends the payload to topic"
[{:keys [db] :as cofx} chat-id success-event payload]
(let [{:keys [web3]} db]
{:shh/send-public-message [{:web3 web3
:success-event success-event
:src (accounts.db/current-public-key cofx)
:chat chat-id
:payload payload}]}))
(fx/defn send-with-pubkey
"Sends the payload using asymetric key (account `:public-key` in db) and fixed discovery topic"
[{:keys [db] :as cofx} {:keys [payload chat-id success-event]}]
(let [{:keys [web3]} db]
{:shh/post [{:web3 web3
:success-event success-event
:message (merge {:sig (accounts.db/current-public-key cofx)
:pubKey chat-id
:payload payload
:topic (transport.utils/get-topic constants/contact-discovery)}
whisper-opts)}]}))
(let [pfs? (get-in db [:account/account :settings :pfs?])]
(if pfs?
(send-direct-message cofx
chat-id
success-event
payload)
{:shh/post [{:web3 web3
:success-event success-event
:message (merge {:sig (accounts.db/current-public-key cofx)
:pubKey chat-id
:payload payload
:topic (transport.utils/get-topic constants/contact-discovery)}
whisper-opts)}]}))))
(defrecord Message [content content-type message-type clock-value timestamp]
StatusMessage
(send [this chat-id {:keys [message-id] :as cofx}]
(let [dev-mode? (get-in cofx [:db :account/account :dev-mode?])
pfs? (get-in cofx [:db :account/account :settings :pfs?])
current-public-key (accounts.db/current-public-key cofx)
params {:chat-id chat-id
:payload this
@ -95,25 +107,13 @@
message-type]}]
(case message-type
:public-group-user-message
(if pfs?
(send-public-message
cofx
chat-id
(:success-event params)
this)
(send-with-sym-key cofx params))
(send-with-sym-key cofx params)
:user-message
(if pfs?
(send-direct-message
cofx
chat-id
(:success-event params)
this)
(fx/merge cofx
#(when (config/pairing-enabled? dev-mode?)
(send-direct-message % current-public-key nil this))
(send-with-pubkey params))))))
(fx/merge cofx
#(when (config/pairing-enabled? dev-mode?)
(send-direct-message % current-public-key nil this))
(send-with-pubkey params)))))
(receive [this chat-id signature _ cofx]
{:chat-received-message/add-fx
[(assoc (into {} this)
@ -134,14 +134,8 @@
(defrecord MessagesSeen [message-ids]
StatusMessage
(send [this chat-id cofx]
(let [pfs? (get-in cofx [:db :account/account :settings :pfs?])]
(if pfs?
(send-direct-message cofx
chat-id
nil
this)
(send-with-pubkey cofx {:chat-id chat-id
:payload this}))))
(send-with-pubkey cofx {:chat-id chat-id
:payload this}))
(receive [this chat-id signature _ cofx]
(chat/receive-seen cofx chat-id signature this))
(validate [this]

View File

@ -24,6 +24,7 @@
:navigation-stack '()
:contacts/contacts {}
:pairing/installations {}
:contact-recovery/pop-up #{}
:qr-codes {}
:group/selected-contacts #{}
:chats {}

View File

@ -0,0 +1,38 @@
(ns status-im.test.contact-recovery.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.contact-recovery.core :as contact-recovery]))
(deftest show-contact-recovery-fx
(let [public-key "pk"]
(testing "pfs is not enabled"
(testing "no pop up is displayed"
(let [actual (contact-recovery/show-contact-recovery-fx {:db {:contact-recovery/pop-up #{}}} public-key)]
(testing "it does nothing"
(is (not (:db actual)))
(is (not (:contact-recovery/show-contact-recovery-message actual)))))))
(testing "no pop up is displayed"
(let [cofx {:db {:contact-recovery/pop-up #{}
:account/account {:settings {:pfs? true}}}}
actual (contact-recovery/show-contact-recovery-fx cofx public-key)]
(testing "it sets the pop up as displayed"
(is (get-in actual [:db :contact-recovery/pop-up public-key])))
(testing "it adds an fx for fetching the contact"
(is (= public-key (:contact-recovery/show-contact-recovery-message actual))))))
(testing "pop up is already displayed"
(let [actual (contact-recovery/show-contact-recovery-fx {:db {:contact-recovery/pop-up #{public-key}}} public-key)]
(testing "it does nothing"
(is (not (:db actual)))
(is (not (:contact-recovery/show-contact-recovery-message actual))))))))
(deftest show-contact-recovery-message
(let [public-key "pk"]
(testing "pfs is enabled"
(let [cofx {:db {:account/account {:settings {:pfs? true}}}}
actual (contact-recovery/show-contact-recovery-message cofx public-key)]
(testing "it shows a pop up"
(is (:ui/show-confirmation actual)))))
(testing "pfs is not enabled"
(let [cofx {:db {:account/account {:settings {}}}}
actual (contact-recovery/show-contact-recovery-message cofx public-key)]
(testing "it shows a pop up"
(is (not (:ui/show-confirmation actual))))))))

View File

@ -56,6 +56,7 @@
[status-im.test.utils.fx]
[status-im.test.accounts.recover.core]
[status-im.test.hardwallet.core]
[status-im.test.contact-recovery.core]
[status-im.test.ui.screens.currency-settings.models]
[status-im.test.ui.screens.wallet.db]))
@ -124,5 +125,6 @@
'status-im.test.ui.screens.currency-settings.models
'status-im.test.ui.screens.wallet.db
'status-im.test.browser.core
'status-im.test.contact-recovery.core
'status-im.test.extensions.ethereum
'status-im.test.browser.permissions)

View File

@ -15,6 +15,8 @@
"sync-all-devices": "Sync all devices",
"syncing-devices": "Syncing...",
"paired-devices": "Paired devices",
"contact-recovery-title": "{{name}} has sent you a message",
"contact-recovery-content": "{{name}} has sent you a message but did not include this device.\nThis might happen if you have more than 3 devices, you haven't paired your devices correctly or you just recovered your account.\nPlease make sure your devices are paired correctly and click Add to contacts to notify the user of this device.",
"pairing-maximum-number-reached-title": "Max number of devices reached",
"pairing-maximum-number-reached-content": "Please disable one of your devices before enabling a new one.",
"pairing-new-installation-detected-title": "New device detected",