Enable pairing & contact recovery

This PR enables pairing outside of dev-mode and contact-recovery, which
is useful in the case a new device is added or re-installed.

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2019-01-29 10:42:53 +01:00
parent ac758f5348
commit 13b04f17eb
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
12 changed files with 118 additions and 119 deletions

View File

@ -1,42 +1,66 @@
(ns status-im.contact-recovery.core
"This namespace handles the case where a user has just recovered their account
and is not able to decrypt messages, as the encryption is device-to-device.
Upon receiving this message, an empty message is sent back carrying device information
which will tell the other peer to target this device as well"
(: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.config :as config]
[status-im.utils.fx :as fx]
[status-im.accounts.db :as accounts.db]
[status-im.contact.core :as models.contact]))
;; How long do we wait until we process a contact-recovery again?
(def contact-recovery-interval-ms (* 6 60 60 1000))
(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?])]
(defn handle-contact-recovery-fx
"Check that a contact-recovery for the given user is not already in process, if not
fetch from db and check"
[{:keys [db now] :as cofx} public-key]
(let [my-public-key (accounts.db/current-public-key cofx)]
(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})))
:contact-recovery/handle-recovery [now 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 notified-recently?
"We don't want to notify the user each time, so we wait an interval before
sending a message again"
[now public-key]
(let [{:keys [timestamp]} (data-store.contact-recovery/get-contact-recovery-by-id public-key)]
(and timestamp
(< contact-recovery-interval-ms (- now timestamp)))))
(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])))
(defn handle-recovery-fx [now public-key]
(when-not (notified-recently? now public-key)
(re-frame/dispatch [:contact-recovery.callback/handle-recovery public-key])))
(fx/defn notify-user
"Send an empty message to the user, which will carry device information"
[cofx public-key]
(let [{:keys [web3]} (:db cofx)
current-public-key (accounts.db/current-public-key cofx)]
{:shh/send-direct-message
[{:web3 web3
:src current-public-key
:dst public-key
:payload ""}]}))
(re-frame/reg-fx
:contact-recovery/show-contact-recovery-message
show-contact-recovery-message-fx)
:contact-recovery/handle-recovery
(fn [[now public-key]]
(handle-recovery-fx now public-key)))
(fx/defn save-contact-recovery [{:keys [now]} public-key]
{:data-store/tx [(data-store.contact-recovery/save-contact-recovery-tx {:timestamp now
@ -46,18 +70,18 @@
(fx/merge
cofx
(prompt-dismissed public-key)
(save-contact-recovery public-key)))
(save-contact-recovery public-key)
(notify-user 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)
(fx/defn handle-recovery [{:keys [db] :as cofx} public-key]
(let [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)
:confirm-button-text (i18n/label :t/notify)
: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))))
(if config/show-contact-recovery-pop-up?
(fx/merge cofx popup)
(prompt-accepted cofx public-key))))

View File

@ -2,7 +2,9 @@
(: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)))
(-> @core/account-realm
(core/get-by-field :contact-recovery :id public-key)
(core/single-clj :contact-recovery)))
(defn save-contact-recovery-tx
"Returns tx function for saving a contact-recovery"

View File

@ -1568,10 +1568,7 @@
: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))))
(contact-recovery/prompt-accepted cofx public-key)))
(handlers/register-handler-fx
:contact-recovery.ui/prompt-dismissed
@ -1579,10 +1576,10 @@
(contact-recovery/prompt-dismissed cofx public-key)))
(handlers/register-handler-fx
:contact-recovery.callback/show-contact-recovery-message
:contact-recovery.callback/handle-recovery
[(re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ public-key]]
(contact-recovery/show-contact-recovery-message cofx public-key)))
(contact-recovery/handle-recovery cofx public-key)))
(handlers/register-handler-fx
:stickers/load-sticker-pack-success
@ -1609,4 +1606,4 @@
(handlers/register-handler-fx
:stickers/select-pack
(fn [{:keys [db]} [_ id]]
{:db (assoc db :stickers/selected-pack id)}))
{:db (assoc db :stickers/selected-pack id)}))

View File

@ -86,21 +86,19 @@
:data-store/tx [(data-store.installations/save updated-installation)]}))
(defn handle-bundles-added [{:keys [db] :as cofx} bundle]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
(when (config/pairing-enabled? dev-mode?)
(let [installation-id (:installationID bundle)
new-installation {:installation-id installation-id
:has-bundle? true}]
(when
(and (= (:identity bundle)
(accounts.db/current-public-key cofx))
(not= (get-in db [:account/account :installation-id]) installation-id)
(not (get-in db [:pairing/installations installation-id])))
(fx/merge cofx
(upsert-installation new-installation)
#(when-not (or (get-in % [:db :pairing/prompt-user-pop-up])
(= :installations (:view-id db)))
(prompt-user-on-new-installation %))))))))
(let [installation-id (:installationID bundle)
new-installation {:installation-id installation-id
:has-bundle? true}]
(when
(and (= (:identity bundle)
(accounts.db/current-public-key cofx))
(not= (get-in db [:account/account :installation-id]) installation-id)
(not (get-in db [:pairing/installations installation-id])))
(fx/merge cofx
(upsert-installation new-installation)
#(when-not (or (get-in % [:db :pairing/prompt-user-pop-up])
(= :installations (:view-id db)))
(prompt-user-on-new-installation %))))))
(defn sync-installation-account-message [{:keys [db]}]
(let [account (-> db
@ -191,18 +189,15 @@
(fx/defn send-sync-installation [cofx payload]
(let [{:keys [web3]} (:db cofx)
current-public-key (accounts.db/current-public-key cofx)]
{:shh/send-direct-message
[{:web3 web3
:src current-public-key
:dst current-public-key
[{:web3 web3
:src current-public-key
:dst current-public-key
:payload payload}]}))
(fx/defn send-installation-message-fx [cofx payload]
(let [dev-mode? (get-in cofx [:db :account/account :dev-mode?])]
(when (and (config/pairing-enabled? dev-mode?)
(has-paired-installations? cofx))
(protocol/send payload nil cofx))))
(when (has-paired-installations? cofx)
(protocol/send payload nil cofx)))
(fx/defn sync-public-chat [cofx chat-id]
(let [sync-message (transport.pairing/SyncInstallation. {} {} {:public? true
@ -230,30 +225,26 @@
contacts))
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts account chat]} sender]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
(when (and (config/pairing-enabled? dev-mode?)
(= sender (accounts.db/current-public-key cofx)))
(let [new-contacts (merge-contacts (:contacts/contacts db) (ensure-photo-path contacts))
new-account (merge-account (:account/account db) account)]
(fx/merge cofx
{:db (assoc db
:contacts/contacts new-contacts
:account/account new-account)
:data-store/base-tx [(data-store.accounts/save-account-tx new-account)]
:data-store/tx [(data-store.contacts/save-contacts-tx (vals new-contacts))]}
#(when (:public? chat)
(models.chat/start-public-chat % (:chat-id chat) {:dont-navigate? true})))))))
(when (= sender (accounts.db/current-public-key cofx))
(let [new-contacts (merge-contacts (:contacts/contacts db) (ensure-photo-path contacts))
new-account (merge-account (:account/account db) account)]
(fx/merge cofx
{:db (assoc db
:contacts/contacts new-contacts
:account/account new-account)
:data-store/base-tx [(data-store.accounts/save-account-tx new-account)]
:data-store/tx [(data-store.contacts/save-contacts-tx (vals new-contacts))]}
#(when (:public? chat)
(models.chat/start-public-chat % (:chat-id chat) {:dont-navigate? true}))))))
(defn handle-pair-installation [{:keys [db] :as cofx} {:keys [name installation-id device-type]} timestamp sender]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
(when (and (config/pairing-enabled? dev-mode?)
(= sender (accounts.db/current-public-key cofx))
(not= (get-in db [:account/account :installation-id]) installation-id))
(let [installation {:installation-id installation-id
:name name
:device-type device-type
:last-paired timestamp}]
(upsert-installation cofx installation)))))
(when (and (= sender (accounts.db/current-public-key cofx))
(not= (get-in db [:account/account :installation-id]) installation-id))
(let [installation {:installation-id installation-id
:name name
:device-type device-type
:last-paired timestamp}]
(upsert-installation cofx installation))))
(fx/defn set-name [{:keys [db] :as cofx} installation-name]
(let [new-account (assoc (get-in cofx [:db :account/account]) :installation-name installation-name)]

View File

@ -71,6 +71,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))
"messages.decrypt.failed" (contact-recovery/handle-contact-recovery-fx cofx (:sender event))
"discovery.summary" (summary cofx event)
(log/debug "Event " type " not handled"))))

View File

@ -90,8 +90,7 @@
(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?])
current-public-key (accounts.db/current-public-key cofx)
(let [current-public-key (accounts.db/current-public-key cofx)
params {:chat-id chat-id
:payload this
:success-event [:transport/message-sent
@ -104,8 +103,7 @@
:user-message
(fx/merge cofx
#(when (config/pairing-enabled? dev-mode?)
(send-direct-message % current-public-key nil this))
(send-direct-message current-public-key nil this)
(send-with-pubkey params)))))
(receive [this chat-id signature _ cofx]
{:chat-received-message/add-fx

View File

@ -203,11 +203,10 @@
installation-id [:pairing/installation-id]
installation-name [:pairing/installation-name]]
[react/scroll-view
(when (config/pairing-enabled? true)
(installations-section
installation-id
installation-name
installations))]))
(installations-section
installation-id
installation-name
installations)]))
(views/defview backup-recovery-phrase []
[profile.recovery/backup-seed])

View File

@ -140,8 +140,8 @@
(if utils.platform/ios?
;; On IOS switches seems to be broken, they take up value of dev-mode? (so if dev mode is on they all show to be on).
;; Replacing therefore with checkbox until I have more time to investigate
(checkbox.views/plain-checkbox {:checked? enabled?
:on-value-change (partial toggle-enabled! installation-id enabled?)})
(checkbox.views/checkbox {:checked? enabled?
:on-value-change (partial toggle-enabled! installation-id enabled?)})
[react/switch {:on-tint-color colors/blue
:value enabled?
:on-value-change (partial toggle-enabled! installation-id enabled?)}])]]])

View File

@ -127,13 +127,11 @@
{:label-kw :t/backup-your-recovery-phrase
:action-fn #(re-frame/dispatch [:navigate-to :backup-seed])
:icon-content [components.common/counter {:size 22} 1]}])
(when (config/pairing-enabled? dev-mode?)
[profile.components/settings-item-separator])
(when (config/pairing-enabled? dev-mode?)
[profile.components/settings-item
{:label-kw :t/devices
:action-fn #(re-frame/dispatch [:navigate-to :installations])
:accessibility-label :pairing-settings-button}])
[profile.components/settings-item-separator]
[profile.components/settings-item
{:label-kw :t/devices
:action-fn #(re-frame/dispatch [:navigate-to :installations])
:accessibility-label :pairing-settings-button}]
[profile.components/settings-item-separator]
[profile.components/settings-switch-item
{:label-kw :t/web3-opt-in

View File

@ -18,9 +18,7 @@
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
(def rpc-networks-only? (enabled? (get-config :RPC_NETWORKS_ONLY "1")))
(def group-chats-publish-to-topic? (enabled? (get-config :GROUP_CHATS_PUBLISH_TO_TOPIC "0")))
(defn pairing-enabled? [dev-mode?]
(and (enabled? (get-config :PAIRING_ENABLED "0"))
(or dev-mode? platform/desktop?)))
(def show-contact-recovery-pop-up? (enabled? (get-config :SHOW_CONTACT_RECOVERY_POPUP)))
(def mailserver-confirmations-enabled? (enabled? (get-config :MAILSERVER_CONFIRMATIONS_ENABLED)))
(def mainnet-warning-enabled? (enabled? (get-config :MAINNET_WARNING_ENABLED 0)))
(def pfs-encryption-enabled? (enabled? (get-config :PFS_ENCRYPTION_ENABLED "0")))

View File

@ -1,38 +1,29 @@
(ns status-im.test.contact-recovery.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.config :as config]
[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 #{}
(testing "no contact-recovery in place"
(let [cofx {:now "now"
:db {:contact-recovery/pop-up #{}
:account/account {:settings {:pfs? true}}}}
actual (contact-recovery/show-contact-recovery-fx cofx public-key)]
actual (contact-recovery/handle-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)]
(is (= ["now" public-key] (:contact-recovery/handle-recovery actual))))))
(testing "contact recovery is in place"
(let [actual (contact-recovery/handle-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)]
(with-redefs [config/show-contact-recovery-pop-up? true]
(let [cofx {:db {}}
actual (contact-recovery/handle-recovery 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))))))))
(is (:ui/show-confirmation actual)))))))

View File

@ -15,9 +15,10 @@
"syncing-disabled": "Syncing disabled",
"sync-all-devices": "Sync all devices",
"syncing-devices": "Syncing...",
"notify": "Notify",
"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.",
"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 Notify to let the user know 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",