multiaccounts refactoring S3, refactor keychain and touchid, move and refactor create/recover/login profile (#16448)

* multiaccounts refactoring S3 E1, refactor keychain and touchid,simplify app init flow, refactor biometric flow
* S3 E2 move and refactor create/recover/login methods
This commit is contained in:
flexsurfer 2023-07-06 11:25:57 +02:00 committed by GitHub
parent 229a806f12
commit 4decba8d00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 818 additions and 2035 deletions

View File

@ -833,6 +833,37 @@ void _SaveAccountAndLogin(const FunctionCallbackInfo<Value>& args) {
}
void _CreateAccountAndLogin(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 1) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for SaveAccountAndLogin")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'settingsJSON'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
// Call exported Go function, which returns a C string
char *c = CreateAccountAndLogin(arg0);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _GenerateAlias(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@ -1929,6 +1960,7 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "hashMessage", _HashMessage);
NODE_SET_METHOD(exports, "resetChainData", _ResetChainData);
NODE_SET_METHOD(exports, "saveAccountAndLogin", _SaveAccountAndLogin);
NODE_SET_METHOD(exports, "createAccountAndLogin", _CreateAccountAndLogin);
NODE_SET_METHOD(exports, "generateAlias", _GenerateAlias);
NODE_SET_METHOD(exports, "validateMnemonic", _ValidateMnemonic);
NODE_SET_METHOD(exports, "multiformatSerializePublicKey", _MultiformatSerializePublicKey);

View File

@ -0,0 +1,78 @@
(ns react-native.keychain
(:require ["react-native-keychain" :as react-native-keychain]
[clojure.string :as string]
[taoensso.timbre :as log]))
;; ********************************************************************************
;; Storing / Retrieving a user password to/from Keychain
;; ********************************************************************************
;;
;; We are using set/get/reset internet credentials there because they are bound
;; to an address (`server`) property.
(defn enum-val
[enum-name value-name]
(get-in (js->clj ^js react-native-keychain) [enum-name value-name]))
;; We need a more strict access mode for keychain entries that save user password.
;; iOS
;; see this article for more details:
;; https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility?language=objc
(def keychain-restricted-availability
;; From Apple's documentation:
;; > The kSecAttrAccessible attribute enables you to control item availability
;; > relative to the lock state of the device.
;; > It also lets you specify eligibility for restoration to a new device.
;; > If the attribute ends with the string ThisDeviceOnly,
;; > the item can be restored to the same device that created a backup,
;; > but it isnt migrated when restoring another devices backup data.
;; > ...
;; > For extremely sensitive data
;; > THAT YOU NEVER WANT STORED IN iCloud,
;; > you might choose kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly.
;; That is exactly what we use there.
;; Note that the password won't be stored if the device isn't locked by a passcode.
#js {:accessible (enum-val "ACCESSIBLE" "WHEN_PASSCODE_SET_THIS_DEVICE_ONLY")})
(def keychain-secure-hardware
;; (Android) Requires storing the encryption key for the entry in secure hardware
;; or StrongBox (see https://developer.android.com/training/articles/keystore#ExtractionPrevention)
"SECURE_HARDWARE")
;; Android only
(defn secure-hardware-available?
[callback]
(-> (.getSecurityLevel ^js react-native-keychain)
(.then (fn [level] (callback (= level keychain-secure-hardware))))))
;; iOS only
(defn device-encrypted?
[callback]
(-> (.canImplyAuthentication
^js react-native-keychain
(clj->js
{:authenticationType
(enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")}))
(.then callback)))
(defn save-credentials
"Stores the credentials for the address to the Keychain"
[server username password callback]
(-> (.setInternetCredentials ^js react-native-keychain
(string/lower-case server)
username
password
keychain-secure-hardware
keychain-restricted-availability)
(.then callback)))
(defn get-credentials
"Gets the credentials for a specified server from the Keychain"
[server callback]
(-> (.getInternetCredentials ^js react-native-keychain (string/lower-case server))
(.then callback)))
(defn reset-credentials
[server]
(-> (.resetInternetCredentials ^js react-native-keychain (string/lower-case server))
(.then #(when-not % (log/error (str "Error while clearing saved password."))))))

View File

@ -0,0 +1,19 @@
(ns react-native.touch-id
(:require ["react-native-touch-id" :default touchid]))
;; currently, for android, react-native-touch-id
;; is not returning supported biometric type
;; defaulting to :fingerprint
(def android-default-support :fingerprint)
(defn get-supported-type
[callback]
(-> (.isSupported ^js touchid)
(.then #(callback (or (keyword %) android-default-support)))
(.catch #(callback nil))))
(defn authenticate
[{:keys [on-success on-fail reason options]}]
(-> (.authenticate ^js touchid reason (clj->js options))
(.then #(when on-success (on-success)))
(.catch #(when on-fail (on-fail (aget % "code"))))))

View File

@ -23,7 +23,6 @@
status-im.log-level.core
status-im.mailserver.constants
[status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.biometric.core :as biometric]
status-im.multiaccounts.login.core
status-im.multiaccounts.logout.core
[status-im.multiaccounts.model :as multiaccounts.model]
@ -63,7 +62,8 @@
[react-native.platform :as platform]
status-im2.contexts.chat.home.events
status-im2.contexts.communities.home.events
status-im.ui.components.invite.events))
status-im.ui.components.invite.events
[status-im2.common.biometric.events :as biometric]))
(re-frame/reg-fx
:dismiss-keyboard
@ -124,33 +124,26 @@
[(get-in db [:profile/profile :appearance])
(:view-id db) true]})))
(def authentication-options
{:reason (i18n/label :t/biometric-auth-reason-login)})
(defn- on-biometric-auth-result
[{:keys [bioauth-success bioauth-code bioauth-message]}]
(when-not bioauth-success
(if (= bioauth-code "USER_FALLBACK")
(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
(utils/show-confirmation
{:title (i18n/label :t/biometric-auth-confirm-title)
:content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message))
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
:on-accept #(biometric/authenticate nil
on-biometric-auth-result
authentication-options)
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])}))))
(defn- on-biometric-auth-fail
[{:keys [code]}]
(if (= code "USER_FALLBACK")
(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
(utils/show-confirmation
{:title (i18n/label :t/biometric-auth-confirm-title)
:content (i18n/label :t/biometric-auth-confirm-message)
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
:on-accept #(biometric/authenticate nil {:on-fail on-biometric-auth-fail})
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))
(rf/defn on-return-from-background
[{:keys [db now] :as cofx}]
(let [new-account? (get db :onboarding-2/new-account?)
app-in-background-since (get db :app-in-background-since)
signed-up? (get-in db [:profile/profile :signed-up?])
biometric-auth? (= (:auth-method db) "biometric")
requires-bio-auth (and
signed-up?
biometric-auth?
(= (:auth-method db) "biometric")
(some? app-in-background-since)
(>= (- now app-in-background-since)
constants/ms-in-bg-for-require-bioauth))]
@ -163,7 +156,7 @@
#(when-let [chat-id (:current-chat-id db)]
{:dispatch [:chat/mark-all-as-read chat-id]})
#(when requires-bio-auth
(biometric/authenticate % on-biometric-auth-result authentication-options)))))
(biometric/authenticate % {:on-fail on-biometric-auth-fail})))))
(rf/defn on-going-in-background
[{:keys [db now]}]
@ -274,53 +267,3 @@
cofx
(navigation/open-modal :buy-crypto nil)
(wallet/keep-watching-history)))
;; Information Box
(def closable-information-boxes
"[{:id information box id
:global? true/false (close information box across all profiles)}]"
[])
(defn information-box-id-hash
[id public-key global?]
(if global?
(hash id)
(hash (str public-key id))))
(rf/defn close-information-box
{:events [:close-information-box]}
[{:keys [db]} id global?]
(let [public-key (get-in db [:profile/profile :public-key])
hash (information-box-id-hash id public-key global?)]
{::async-storage/set! {hash true}
:db (assoc-in db [:information-box-states id] true)}))
(rf/defn information-box-states-loaded
{:events [:information-box-states-loaded]}
[{:keys [db]} hashes states]
{:db (assoc db
:information-box-states
(reduce
(fn [acc [id hash]]
(assoc acc id (get states hash)))
{}
hashes))})
(rf/defn load-information-box-states
{:events [:load-information-box-states]}
[{:keys [db]}]
(let [public-key (get-in db [:profile/profile :public-key])
{:keys [keys hashes]} (reduce (fn [acc {:keys [id global?]}]
(let [hash (information-box-id-hash
id
public-key
global?)]
(-> acc
(assoc-in [:hashes id] hash)
(update :keys #(conj % hash)))))
{}
closable-information-boxes)]
{::async-storage/get {:keys keys
:cb #(re-frame/dispatch
[:information-box-states-loaded hashes %])}}))

View File

@ -26,13 +26,10 @@
[]
(rf/dispatch [:app-started]))
(defn generate-and-derive-addresses!
[]
(rf/dispatch [:generate-and-derive-addresses]))
(defn create-multiaccount!
[]
(rf/dispatch [:create-multiaccount password]))
(rf/dispatch [:profile.create/create-and-login
{:display-name account-name :password password :color "blue"}]))
(defn assert-app-initialized
[]
@ -82,15 +79,12 @@
(initialize-app!) ; initialize app
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!) ; generate 5 new keys
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(logout!)
(rf-test/wait-for [::logout/logout-method])))))
(deftest create-community-test
(log/info "====== create-community-test ==================")
@ -98,22 +92,19 @@
(initialize-app!) ; initialize app
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!) ; generate 5 new keys
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:legacy-only-for-e2e/open-create-community])
(doseq [[k v] (dissoc community :membership)]
(rf/dispatch-sync [:status-im.communities.core/create-field k v]))
(rf/dispatch [:status-im.communities.core/create-confirmation-pressed])
(rf-test/wait-for
[:status-im.communities.core/community-created]
(assert-community-created)
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:legacy-only-for-e2e/open-create-community])
(doseq [[k v] (dissoc community :membership)]
(rf/dispatch-sync [:status-im.communities.core/create-field k v]))
(rf/dispatch [:status-im.communities.core/create-confirmation-pressed])
(rf-test/wait-for
[:status-im.communities.core/community-created]
(assert-community-created)
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest create-wallet-account-test
(log/info "====== create-wallet-account-test ==================")
@ -121,19 +112,16 @@
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!) ; generate 5 new keys
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(create-new-account!) ; create a new account
(rf-test/wait-for
[:wallet.accounts/account-stored]
(assert-new-account-created) ; assert account was created
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(create-new-account!) ; create a new account
(rf-test/wait-for
[:wallet.accounts/account-stored]
(assert-new-account-created) ; assert account was created
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest back-up-seed-phrase-test
(log/info "========= back-up-seed-phrase-test ==================")
@ -141,30 +129,27 @@
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!)
(create-multiaccount!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user
(rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words
(let [{:keys [mnemonic]} @(rf/subscribe [:profile/profile])
seed @(rf/subscribe [:my-profile/seed])
word1 (second (:first-word seed))
word2 (second (:second-word seed))]
(is (= 12 (count (string/split mnemonic #" ")))) ; assert 12-word seed phrase
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word1])
(rf/dispatch-sync [:my-profile/set-step :second-word])
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word2])
;; TODO: refactor (defn next-handler) & (defn enter-word) to improve test coverage?
(rf/dispatch [:my-profile/finish])
(rf-test/wait-for
[:my-profile/finish-success]
(is (nil? @(rf/subscribe [:mnemonic]))) ; assert seed phrase has been removed
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))))
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user
(rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words
(let [{:keys [mnemonic]} @(rf/subscribe [:profile/profile])
seed @(rf/subscribe [:my-profile/seed])
word1 (second (:first-word seed))
word2 (second (:second-word seed))]
(is (= 12 (count (string/split mnemonic #" ")))) ; assert 12-word seed phrase
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word1])
(rf/dispatch-sync [:my-profile/set-step :second-word])
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word2])
;; TODO: refactor (defn next-handler) & (defn enter-word) to improve test coverage?
(rf/dispatch [:my-profile/finish])
(rf-test/wait-for
[:my-profile/finish-success]
(is (nil? @(rf/subscribe [:mnemonic]))) ; assert seed phrase has been removed
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(def multiaccount-name "Narrow Frail Lemming")
(def multiaccount-mnemonic
@ -180,20 +165,17 @@
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!)
(create-multiaccount!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(create-multiaccount!)
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest delete-chat-test
(log/info "========= delete-chat-test ==================")
@ -201,23 +183,20 @@
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!)
(create-multiaccount!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(create-multiaccount!)
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id])
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id])
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest mute-chat-test
(log/info "========= mute-chat-test ==================")
@ -225,29 +204,26 @@
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!)
(create-multiaccount!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(create-multiaccount!)
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted])
(rf-test/wait-for
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted])
[:chat/mute-successfully]
(is @(rf/subscribe [:chats/muted chat-id]))
(rf/dispatch-sync [:chat.ui/mute chat-id false])
(rf-test/wait-for
[:chat/mute-successfully]
(is @(rf/subscribe [:chats/muted chat-id]))
(rf/dispatch-sync [:chat.ui/mute chat-id false])
(rf-test/wait-for
[:chat/mute-successfully]
(is (not @(rf/subscribe [:chats/muted chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))))
(is (not @(rf/subscribe [:chats/muted chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))))
(deftest add-contact-test
(log/info "========= add-contact-test ==================")
@ -260,27 +236,24 @@
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(generate-and-derive-addresses!)
(create-multiaccount!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(create-multiaccount!)
[::transport/messenger-started]
(assert-messenger-started)
;; search for contact using compressed key
(rf/dispatch [:contacts/set-new-identity compressed-key])
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
;; search for contact using compressed key
(rf/dispatch [:contacts/set-new-identity compressed-key])
[:contacts/set-new-identity-success]
(let [new-identity @(rf/subscribe [:contacts/new-identity])]
(is (= public-key (:public-key new-identity)))
(is (= :valid (:state new-identity))))
;; click 'view profile' button
(rf/dispatch [:chat.ui/show-profile public-key])
(rf-test/wait-for
[:contacts/set-new-identity-success]
(let [new-identity @(rf/subscribe [:contacts/new-identity])]
(is (= public-key (:public-key new-identity)))
(is (= :valid (:state new-identity))))
;; click 'view profile' button
(rf/dispatch [:chat.ui/show-profile public-key])
[:contacts/build-contact]
(rf-test/wait-for
[:contacts/build-contact]
(rf-test/wait-for
[:contacts/contact-built]
(let [contact @(rf/subscribe [:contacts/current-contact])]
(is (= three-words-name (:primary-name contact))))
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))))))
[:contacts/contact-built]
(let [contact @(rf/subscribe [:contacts/current-contact])]
(is (= three-words-name (:primary-name contact))))
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))))

View File

@ -14,7 +14,6 @@
[status-im.keycard.sign :as sign]
status-im.keycard.unpair
[status-im.keycard.wallet :as wallet]
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[utils.re-frame :as rf]
[utils.datetime :as datetime]
@ -589,7 +588,8 @@
(when (and (= card-state :profile/profile)
(= flow :import))
(if (common/find-multiaccount-by-key-uid db key-uid)
(multiaccounts.recover/show-existing-multiaccount-alert key-uid)
;; reimplement
;;(multiaccounts.recover/show-existing-multiaccount-alert key-uid)
(if paired?
(load-recovery-pin-screen)
(recovery/load-pair-screen))))
@ -681,3 +681,11 @@
{:events [:keycard.callback/stop-nfc-failure]}
[{:keys [db]} _]
(log/debug "[keycard] nfc failed stopping")) ;; leave current value on :nfc-running
(rf/defn init
{:events [:keycard/init]}
[_]
{:keycard/register-card-events nil
:keycard/check-nfc-support nil
:keycard/check-nfc-enabled nil
:keycard/retrieve-pairings nil})

View File

@ -139,7 +139,6 @@
{:db (-> db
(update :keycard dissoc :flow)
(dissoc :restored-account?))}
(multiaccounts.create/prepare-intro-wizard)
(if (pos? (count accs))
(navigation/navigate-to :get-your-keys nil)
(navigation/set-stack-root :onboarding [:get-your-keys])))))

View File

@ -1,229 +0,0 @@
(ns status-im.multiaccounts.biometric.core
(:require ["react-native-touch-id" :default touchid]
[quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[utils.i18n :as i18n]
[native-module.core :as native-module]
[status-im.popover.core :as popover]
[utils.re-frame :as rf]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]))
;; currently, for android, react-native-touch-id
;; is not returning supported biometric type
;; defaulting to :fingerprint
(def android-default-support :fingerprint)
;;; android blacklist based on device info:
(def deviceinfo (native-module/get-device-model-info))
;; {:model ?
;; :brand "Xiaomi"
;; :build-id "13D15"
;; :device-id "goldfish"
;; more info on https://github.com/react-native-community/react-native-device-info
(def android-device-blacklisted?
(cond
(= (:brand deviceinfo) "bannedbrand") true
:else false))
;; biometric auth config
;; https://github.com/naoufal/react-native-touch-id#authenticatereason-config
(defn- authenticate-options
[ios-fallback-label]
(clj->js (merge
{:unifiedErrors true}
(when platform/ios?
{:passcodeFallback false
:fallbackLabel (or ios-fallback-label "")})
(when platform/android?
{:title (i18n/label :t/biometric-auth-android-title)
:imageColor colors/blue
:imageErrorColor colors/red
:sensorDescription (i18n/label :t/biometric-auth-android-sensor-desc)
:sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc)
:cancelText (i18n/label :cancel)}))))
(defn get-label
[supported-biometric-auth]
(case supported-biometric-auth
:fingerprint (i18n/label :t/biometric-fingerprint)
:FaceID (i18n/label :t/biometric-faceid)
(i18n/label :t/biometric-touchid)))
(defn- get-error-message
"must return an error message for the user"
[touchid-error-code]
(cond
;; no message if user canceled or falled back to password
(= touchid-error-code "USER_CANCELED") nil
(= touchid-error-code "USER_FALLBACK") nil
;; add here more specific errors if needed
;; https://github.com/naoufal/react-native-touch-id#unified-errors
:else (i18n/label :t/biometric-auth-error
{:code touchid-error-code})))
(def success-result
{:bioauth-success true})
(defn- generate-error-result
[touchid-error-obj]
(let [code (aget touchid-error-obj "code")]
{:bioauth-success false
:bioauth-code code
:bioauth-message (get-error-message code)}))
(defn- do-get-supported
[callback]
(-> (.isSupported touchid)
(.then #(callback (or (keyword %) android-default-support)))
(.catch #(callback nil))))
(defn get-supported
[callback]
(log/debug "[biometric] get-supported")
(cond platform/ios? (do-get-supported callback)
platform/android? (if android-device-blacklisted?
(callback nil)
(do-get-supported callback))
:else (callback nil)))
(defn authenticate-fx
([cb]
(authenticate-fx cb nil))
([cb {:keys [reason ios-fallback-label]}]
(log/debug "[biometric] authenticate-fx")
(-> (.authenticate touchid reason (authenticate-options ios-fallback-label))
(.then #(cb success-result))
(.catch #(cb (generate-error-result %))))))
(re-frame/reg-fx
:biometric/get-supported-biometric-auth
(fn []
(let [callback #(re-frame/dispatch [:init.callback/get-supported-biometric-auth-success %])]
;;NOTE: if we can't save user password, we can't support biometrics
(keychain/can-save-user-password?
(fn [can-save?]
(if can-save?
(get-supported callback)
(callback nil)))))))
(rf/defn set-supported-biometric-auth
{:events [:init.callback/get-supported-biometric-auth-success]}
[{:keys [db]} supported-biometric-auth]
{:db (assoc db :supported-biometric-auth supported-biometric-auth)})
(rf/defn authenticate
[_ cb options]
{:biometric-auth/authenticate [cb options]})
(re-frame/reg-fx
:biometric-auth/authenticate
(fn [[cb options]]
(authenticate-fx #(cb %) options)))
(re-frame/reg-fx
:biometric/enable-and-save-password
(fn [{:keys [key-uid
masked-password
on-success
on-error]}]
(-> (keychain/save-user-password!
key-uid
masked-password)
(.then
(fn [_]
(keychain/save-auth-method!
key-uid
keychain/auth-method-biometric)))
(.then
(fn [_]
(when on-success
(on-success))))
(.catch (fn [error]
(when on-error
(on-error error)))))))
(rf/defn update-biometric
[{db :db :as cofx} biometric-auth?]
(let [key-uid (or (get-in db [:profile/profile :key-uid])
(get-in db [:profile/login :key-uid]))]
(rf/merge cofx
(keychain/save-auth-method
key-uid
(if biometric-auth?
keychain/auth-method-biometric
keychain/auth-method-none))
#(when-not biometric-auth?
{:keychain/clear-user-password key-uid}))))
(rf/defn biometric-auth-switched
{:events [:multiaccounts.ui/biometric-auth-switched]}
[cofx biometric-auth?]
(if biometric-auth?
(authenticate
cofx
#(re-frame/dispatch [:biometric-init-done %])
{})
(update-biometric cofx false)))
(rf/defn show-message
[cofx bioauth-message bioauth-code]
(let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code)
(i18n/label :t/grant-face-id-permissions))
bioauth-message)]
(when content
{:utils/show-popup
{:title (i18n/label :t/biometric-auth-login-error-title)
:content content}})))
(rf/defn biometric-init-done
{:events [:biometric-init-done]}
[cofx {:keys [bioauth-success bioauth-message bioauth-code]}]
(if bioauth-success
(if (= keychain/auth-method-password
(get-in cofx [:db :auth-method]))
(update-biometric cofx true)
(popover/show-popover cofx {:view :enable-biometric}))
(show-message cofx bioauth-message bioauth-code)))
(rf/defn biometric-auth
{:events [:biometric-authenticate]}
[cofx]
(authenticate
cofx
#(re-frame/dispatch [:biometric-auth-done %])
{:reason (i18n/label :t/biometric-auth-reason-login)
:ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)}))
(rf/defn enable
{:events [:biometric/enable]}
[cofx]
(rf/merge
cofx
(popover/hide-popover)
(authenticate #(re-frame/dispatch [:biometric/setup-done %]) {})))
(rf/defn disable
{:events [:biometric/disable]}
[{:keys [db] :as cofx}]
(rf/merge
cofx
{:db (-> db
(assoc :auth-method keychain/auth-method-none)
(assoc-in [:profile/login :save-password?] false))}
(popover/hide-popover)))
(rf/defn setup-done
{:events [:biometric/setup-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(log/debug "[biometric] setup-done"
"bioauth-success" bioauth-success
"bioauth-message" bioauth-message
"bioauth-code" bioauth-code)
(if bioauth-success
{:db (assoc db :auth-method keychain/auth-method-biometric-prepare)}
(show-message cofx bioauth-message bioauth-code)))

View File

@ -41,43 +41,6 @@
(fn [cofx _]
(assoc cofx :signing-phrase (signing-phrase/generate))))
(re-frame/reg-fx
::store-multiaccount
(fn [[id key-uid hashed-password callback]]
(native-module/multiaccount-store-derived
id
key-uid
[constants/path-wallet-root
constants/path-eip1581
constants/path-whisper
constants/path-default-wallet]
hashed-password
callback)))
(rf/defn create-multiaccount
{:events [:create-multiaccount]}
[{:keys [db]} key-code]
(let [{:keys [selected-id]} (:intro-wizard db)]
{::store-multiaccount
[selected-id
(some
(fn [{:keys [id key-uid]}]
(when (= id selected-id)
key-uid))
(get-in db [:intro-wizard :multiaccounts]))
(ethereum/sha3 (security/safe-unmask-data key-code))
(fn [result]
(let [derived-data (normalize-derived-data-keys (types/json->clj result))
public-key (get-in derived-data [constants/path-whisper-keyword :public-key])]
(native-module/gfycat-identicon-async
public-key
(fn [name _]
(let [derived-whisper (derived-data constants/path-whisper-keyword)
derived-data-extended (assoc-in derived-data
[constants/path-whisper-keyword]
(assoc derived-whisper :name name))]
(re-frame/dispatch [::store-multiaccount-success key-code derived-data-extended]))))))]}))
(re-frame/reg-fx
:multiaccount-generate-and-derive-addresses
(fn []
@ -116,10 +79,6 @@
(dissoc :recovered-account?))
:multiaccount-generate-and-derive-addresses nil})
(rf/defn prepare-intro-wizard
[{:keys [db]}]
{:db (assoc db :intro-wizard {})})
(rf/defn save-multiaccount-and-login-with-keycard
[_ args]
{:keycard/save-multiaccount-and-login args})
@ -255,27 +214,3 @@
settings
(node/get-new-config db)
accounts-data)))))
(rf/defn store-multiaccount-success
{:events [::store-multiaccount-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::get-signing-phrase)]}
[{:keys [db] :as cofx} password derived]
(rf/merge cofx
{:db (dissoc db :intro-wizard)}
(on-multiaccount-created (assoc (let [{:keys [selected-id multiaccounts]} (:intro-wizard db)]
(some #(when (= selected-id (:id %)) %) multiaccounts))
:derived derived
:recovered (get-in db [:intro-wizard :recovering?]))
password
{:save-mnemonic? true})))
(rf/defn on-key-selected
{:events [:intro-wizard/on-key-selected]}
[{:keys [db]} id]
{:db (assoc-in db [:intro-wizard :selected-id] id)})
(rf/defn on-key-storage-selected
{:events [:intro-wizard/on-key-storage-selected]}
[{:keys [db]} storage-type]
{:db (assoc-in db [:intro-wizard :selected-storage-type] storage-type)})

View File

@ -1,21 +0,0 @@
(ns status-im.multiaccounts.key-storage.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[native-module.core :as native-module]
[status-im.utils.types :as types]
[utils.security.core :as security]))
(re-frame/reg-fx
:key-storage/delete-imported-key
(fn [{:keys [key-uid address password on-success on-error]}]
(let [hashed-pass (ethereum/sha3 (security/safe-unmask-data password))]
(native-module/delete-imported-key
key-uid
(string/lower-case (subs address 2))
hashed-pass
(fn [result]
(let [{:keys [error]} (types/json->clj result)]
(if-not (string/blank? error)
(on-error error)
(on-success))))))))

View File

@ -17,17 +17,14 @@
[status-im.fleet.core :as fleet]
[utils.i18n :as i18n]
[status-im.mobile-sync-settings.core :as mobile-network]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.core :as multiaccounts]
[native-module.core :as native-module]
[status-im.notifications.core :as notifications]
[status-im.popover.core :as popover]
[status-im.signing.eip1559 :as eip1559]
[status-im.transport.core :as transport]
[status-im.ui.components.react :as react]
[status-im2.config :as config]
[utils.re-frame :as rf]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.mobile-sync :as utils.mobile-sync]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
@ -46,7 +43,7 @@
[taoensso.timbre :as log]
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
[utils.security.core :as security]
[status-im.keycard.common :as keycard.common]))
[status-im2.common.keychain.events :as keychain]))
(re-frame/reg-fx
::initialize-transactions-management-enabled
@ -56,22 +53,6 @@
:transactions-management-enabled?
callback))))
(re-frame/reg-fx
::login
(fn [[key-uid _ hashed-password]]
(native-module/login-account {:keyUid key-uid
:password hashed-password
:wakuV2Nameserver "1.1.1.1"
:openseaAPIKey config/opensea-api-key
:poktToken config/POKT_TOKEN
:infuraToken config/INFURA_TOKEN
:alchemyOptimismMainnetToken config/ALCHEMY_OPTIMISM_MAINNET_TOKEN
:alchemyOptimismGoerliToken config/ALCHEMY_OPTIMISM_GOERLI_TOKEN
:alchemyArbitrumMainnetToken config/ALCHEMY_ARBITRUM_MAINNET_TOKEN
:alchemyArbitrumGoerliToken config/ALCHEMY_ARBITRUM_GOERLI_TOKEN})))
(re-frame/reg-fx
::export-db
(fn [[key-uid account-data hashed-password callback]]
@ -177,27 +158,8 @@
(wallet/request-current-block-update))
(prices/update-prices)))
(rf/defn login-local-paired-user
{:events [:multiaccounts.login/local-paired-user]}
[{:keys [db]}]
(let [{:keys [key-uid name password]} (get-in db [:syncing :profile/profile])]
{::login [key-uid
(types/clj->json {:name name
:key-uid key-uid})
password]}))
(rf/defn login
{:events [:multiaccounts.login.ui/password-input-submitted]}
[{:keys [db]}]
(let [{:keys [key-uid password name]} (:profile/login db)]
{:db (-> db
(assoc-in [:profile/login :processing] true)
(dissoc :intro-wizard :recovered-account?)
(update :keycard dissoc :flow))
::login [key-uid
(types/clj->json {:name name
:key-uid key-uid})
(ethereum/sha3 (security/safe-unmask-data password))]}))
(rf/defn export-db-submitted
{:events [:multiaccounts.login.ui/export-db-submitted]}
@ -392,7 +354,7 @@
(rf/defn get-node-config-callback
{:events [::get-node-config-callback]}
[{:keys [db] :as cofx} node-config-json]
[{:keys [db]} node-config-json]
(let [node-config (types/json->clj node-config-json)]
{:db (assoc-in db
[:profile/profile :wakuv2-config]
@ -414,14 +376,11 @@
(get db :onboarding-2/new-account?)
{:dispatch [:onboarding-2/finalize-setup]}
(get db :tos/accepted?)
:else
(rf/merge
cofx
(multiaccounts/switch-theme nil :shell-stack)
(navigation/init-root :shell-stack))
:else
{:dispatch [:init-root :tos]})))
(navigation/init-root :shell-stack)))))
(rf/defn get-settings-callback
{:events [::get-settings-callback]}
@ -448,7 +407,6 @@
#(do (re-frame/dispatch [:chats-list/load-success %])
(rf/dispatch [:communities/get-user-requests-to-join])
(re-frame/dispatch [::get-chats-callback]))})
(initialize-wallet-connect)
(get-node-config)
(communities/fetch)
(contract-communities/fetch-contract-communities)
@ -530,11 +488,9 @@
(defn get-new-auth-method
[auth-method save-password?]
(when save-password?
(when-not (or (= keychain/auth-method-biometric auth-method)
(= keychain/auth-method-password auth-method))
(if (= auth-method keychain/auth-method-biometric-prepare)
keychain/auth-method-biometric
keychain/auth-method-password))))
(when-not (= keychain/auth-method-biometric auth-method)
(when (= auth-method keychain/auth-method-biometric-prepare)
keychain/auth-method-biometric))))
(rf/defn login-only-events
[{:keys [db] :as cofx} key-uid password save-password?]
@ -549,8 +505,6 @@
[{:method "settings_getSettings"
:on-success #(re-frame/dispatch [::get-settings-callback %])}]}
(notifications/load-notification-preferences)
(when save-password?
(keychain/save-user-password key-uid password))
(keychain/save-auth-method key-uid
(or new-auth-method auth-method keychain/auth-method-none)))))
@ -560,14 +514,13 @@
db
{:keys [creating?]} (:profile/login db)
first-account? (and creating? (empty? profiles-overview))
tos-accepted? (get db :tos/accepted?)
{:networks/keys [current-network networks]} db
network-id (str (get-in networks [current-network :config :NetworkId]))]
(shell.utils/change-selected-stack-id :communities-stack true nil)
(rf/merge cofx
{:db (-> db
(dissoc :profile/login)
(assoc :tos/next-root :enable-notifications :chats/loading? false)
(assoc :chats/loading? false)
(assoc-in [:profile/profile :multiaccounts/first-account]
first-account?))
::get-tokens [network-id wallet-accounts recovered-account?]}
@ -586,7 +539,7 @@
(boolean (get-in cofx [:db :keycard :flow])))
(defn on-login-update-db
[db login-only? now]
[db now]
(-> db
(dissoc :connectivity/ui-status-properties)
(update :keycard dissoc :from-key-storage-and-migration?)
@ -595,10 +548,6 @@
:card-read-in-progress?
:pin
:profile/profile)
(assoc :tos-accept-next-root
(if login-only?
:shell-stack
:onboarding-notification))
(assoc :logged-in-since now)
(assoc :view-id :home)))
@ -620,7 +569,7 @@
"login-only?" login-only?
"recovered-account?" recovered-account?)
(rf/merge cofx
{:db (on-login-update-db db login-only? now)
{:db (on-login-update-db db now)
:json-rpc/call
[{:method "web3_clientVersion"
:on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]}
@ -636,151 +585,3 @@
(if login-only?
(login-only-events key-uid password save-password?)
(create-only-events recovered-account?)))))
(rf/defn open-login-callback
{:events [:multiaccounts.login.callback/get-user-password-success]}
[{:keys [db] :as cofx} password]
(let [key-uid (get-in db [:profile/login :key-uid])
keycard-account? (boolean (get-in db
[:profile/profiles-overview
key-uid
:keycard-pairing]))
goto-key-storage? (:goto-key-storage? db)]
(if password
(rf/merge
cofx
{:db (update-in db
[:profile/login]
assoc
:password password
:save-password? true)
:set-root :progress}
login)
(rf/merge
cofx
{:db (dissoc db :goto-key-storage?)}
(when keycard-account?
{:db (-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :pin :login] []))})
#(if keycard-account?
{:set-root :multiaccounts-keycard}
{:set-root :profiles})
#(when goto-key-storage?
(navigation/navigate-to % :actions-not-logged-in nil))))))
(rf/defn get-credentials
[{:keys [db] :as cofx} key-uid]
(let [keycard-multiaccount? (boolean (get-in db
[:profile/profiles-overview key-uid :keycard-pairing]))]
(log/debug "[login] get-credentials"
"keycard-multiacc?"
keycard-multiaccount?)
(if keycard-multiaccount?
(keychain/get-keycard-keys cofx key-uid)
(keychain/get-user-password cofx key-uid))))
(rf/defn get-auth-method-success
"Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\""
{:events [:multiaccounts.login/get-auth-method-success]}
[{:keys [db] :as cofx} auth-method]
(let [key-uid (get-in db [:profile/login :key-uid])
keycard-profile? (boolean (get-in db [:profile/profiles-overview key-uid :keycard-pairing]))]
(rf/merge
cofx
{:db (assoc db :auth-method auth-method)}
#(cond
(= auth-method keychain/auth-method-biometric)
(biometric/biometric-auth %)
(= auth-method keychain/auth-method-password)
(get-credentials % key-uid)
(and keycard-profile? (get-in db [:keycard :card-connected?]))
(keycard.common/get-application-info % nil))
(open-login-callback nil))))
(rf/defn biometric-auth-done
{:events [:biometric-auth-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(let [key-uid (get-in db [:profile/login :key-uid])
auth-method (get db :auth-method)]
(log/debug "[biometric] biometric-auth-done"
"bioauth-success" bioauth-success
"bioauth-message" bioauth-message
"bioauth-code" bioauth-code)
(if bioauth-success
(get-credentials cofx key-uid)
(rf/merge cofx
{:db (assoc-in db
[:profile/login :save-password?]
(= auth-method keychain/auth-method-biometric))}
(when-not (= auth-method keychain/auth-method-biometric)
(keychain/save-auth-method key-uid keychain/auth-method-none))
(biometric/show-message bioauth-message bioauth-code)
(open-login-callback nil)))))
(rf/defn save-password
{:events [:multiaccounts/save-password]}
[{:keys [db] :as cofx} save-password?]
(let [bioauth-supported? (boolean (get db :supported-biometric-auth))
previous-auth-method (get db :auth-method)]
(log/debug "[login] save-password"
"save-password?" save-password?
"bioauth-supported?" bioauth-supported?
"previous-auth-method" previous-auth-method)
(rf/merge
cofx
{:db (cond-> db
(not= previous-auth-method
keychain/auth-method-biometric-prepare)
(assoc :auth-method keychain/auth-method-none)
(or save-password?
(not bioauth-supported?)
(and (not save-password?)
bioauth-supported?
(= previous-auth-method keychain/auth-method-none)))
(assoc-in [:profile/login :save-password?] save-password?))}
(when bioauth-supported?
(if save-password?
(popover/show-popover {:view :secure-with-biometric})
(when-not (= previous-auth-method keychain/auth-method-none)
(popover/show-popover {:view :disable-password-saving})))))))
(rf/defn welcome-lets-go
{:events [:welcome-lets-go]}
[_]
{:set-root :shell-stack})
(rf/defn hide-keycard-banner
{:events [:hide-keycard-banner]}
[{:keys [db]}]
{:db (assoc db :keycard/banner-hidden true)
::async-storage/set! {:keycard-banner-hidden true}})
(rf/defn get-keycard-banner-preference-cb
{:events [:get-keycard-banner-preference-cb]}
[{:keys [db]} {:keys [keycard-banner-hidden]}]
{:db (assoc db :keycard/banner-hidden keycard-banner-hidden)})
(rf/defn get-keycard-banner-preference
{:events [:get-keycard-banner-preference]}
[_]
{::async-storage/get {:keys [:keycard-banner-hidden]
:cb #(re-frame/dispatch [:get-keycard-banner-preference-cb %])}})
(rf/defn get-opted-in-to-new-terms-of-service-cb
{:events [:get-opted-in-to-new-terms-of-service-cb]}
[{:keys [db]} {:keys [new-terms-of-service-accepted]}]
{:db (assoc db :tos/accepted? new-terms-of-service-accepted)})
(rf/defn get-opted-in-to-new-terms-of-service
"New TOS sprint https://github.com/status-im/status-mobile/pull/12240"
{:events [:get-opted-in-to-new-terms-of-service]}
[{:keys [db]}]
{::async-storage/get {:keys [:new-terms-of-service-accepted]
:cb #(re-frame/dispatch [:get-opted-in-to-new-terms-of-service-cb %])}})
(rf/defn hide-terms-of-services-opt-in-screen
{:events [:hide-terms-of-services-opt-in-screen]}
[{:keys [db]}]
{::async-storage/set! {:new-terms-of-service-accepted true}
:db (assoc db :tos/accepted? true)})

View File

@ -1,62 +0,0 @@
(ns status-im.multiaccounts.login.core-test
(:require [cljs.test :as test]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.login.core :as login]
[utils.re-frame :as rf]
[status-im.utils.keychain.core :as keychain]))
(test/deftest save-password-test
(test/testing "check save password, biometric unavailable"
(let [initial-cofx {:db {:auth-method keychain/auth-method-none
:supported-biometric-auth false}}
{:keys [db]} (login/save-password initial-cofx true)]
(test/is (= false (contains? db :popover/popover)))
(test/is (= true (get-in db [:profile/login :save-password?])))
(test/testing "uncheck save password"
(let [{:keys [db]} (login/save-password {:db db} false)]
(test/is (= false (contains? db :popover/popover)))
(test/is (= false (get-in db [:profile/login :save-password?])))))))
(test/testing "check save password, biometric available"
(let [initial-cofx {:db {:auth-method keychain/auth-method-none
:supported-biometric-auth true}}
{:keys [db]} (login/save-password initial-cofx true)]
(test/is (= true (get-in db [:profile/login :save-password?])))
(test/testing "enable biometric auth"
(let [{:keys [db] :as res} (biometric/enable {:db db})]
(test/is (contains? res :biometric-auth/authenticate))
(test/is (= false (contains? db :popover/popover)))
(test/testing "biometric auth successfully enabled"
(let [{:keys [db]} (biometric/setup-done
{:db db}
{:bioauth-success true
:bioauth-message nil
:bioauth-code nil})]
(test/is (= keychain/auth-method-biometric-prepare
(:auth-method db)))))
(test/testing "biometric auth canceled"
(let [{:keys [db]} (biometric/setup-done
{:db db}
{:bioauth-success false
:bioauth-message nil
:bioauth-code "USER_CANCELED"})]
(test/is (= nil db) "no db changes")))))))
(test/testing (str "check save password, enable biometric auth,"
"uncheck save password")
(let [initial-cofx {:db {:auth-method keychain/auth-method-none
:supported-biometric-auth true}}
{:keys [db]} (login/save-password (rf/merge
initial-cofx
(login/save-password true)
(biometric/enable)
(biometric/setup-done
{:bioauth-success true
:bioauth-message nil
:bioauth-code nil}))
false)]
(test/is (= true (get-in db [:profile/login :save-password?])))
;; case 2 from https://github.com/status-im/status-mobile/issues/9573
(test/is (= keychain/auth-method-biometric-prepare (:auth-method db)))
(test/testing "disable biometric"
(let [{:keys [db]} (biometric/disable {:db db})]
(test/is (= false (get-in db [:profile/login :save-password?])))
(test/is (= keychain/auth-method-none (:auth-method db))))))))

View File

@ -1,121 +0,0 @@
(ns status-im.multiaccounts.login.data-test)
(def all-contacts
[{:last-updated 1547185503000
:tags #{}
:address "2f88d65f3cb52605a54a833ae118fb1363acccd2"
:name "Darkviolet Lightgreen Halcyon"
:added? true
:last-online 0
:public-key
"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"}
{:last-updated 1547271764000
:address "b267ff8336ac10b3a1986c04a70ff91fb03d0b78"
:name "rv"
:added? true
:last-online 0
:public-key
"0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"}])
(def chats
[{:updated-at nil
:tags #{}
:color "#51d0f0"
:contacts #{}
:last-clock-value 154990121501242
:admins #{}
:members-joined #{}
:name "status"
:membership-updates ()
:unviewed-messages-count 0
:last-message-content-type "text/plain"
:last-message-content {:chat-id "status" :text "darn typos...! "}
:debug? false
:group-chat true
:public? true
:chat-id "status"
:timestamp 1547361080397
:deleted-at-clock-value nil}
{:updated-at nil
:tags #{}
:color "#d37ef4"
:contacts
#{"0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"}
:last-clock-value 154727176928001
:admins #{}
:members-joined #{}
:name "rv"
:membership-updates ()
:unviewed-messages-count 0
:last-message-content-type "text/plain"
:last-message-content
{:chat-id
"0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c"
:text "Hey"}
:debug? false
:group-chat false
:public? false
:chat-id
"0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"
:timestamp 1547271770816
:deleted-at-clock-value nil}
{:updated-at nil
:tags #{}
:color "#7cda00"
:contacts
#{"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"}
:last-clock-value 154718689430301
:admins #{}
:members-joined #{}
:name "Darkviolet Lightgreen Halcyon"
:membership-updates ()
:unviewed-messages-count 0
:last-message-content-type "text/plain"
:last-message-content
{:chat-id
"0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c"
:text "Djndjd"}
:debug? false
:group-chat false
:public? false
:chat-id
"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"
:timestamp 1547186895328
:deleted-at-clock-value nil}])
(def multiaccount
{:last-updated 0
:address "7540c34d6c4082391f12468580a9a4e0724c6755"
:mnemonic "tumble gorilla neglect dumb budget involve tennis ocean diary eagle lady ring"
:custom-bootnodes {}
:signing-phrase "bull exam weed"
:signed-up? true
:name "name"
:last-request nil
:wallet/visible-tokens {:mainnet #{:SNT}
:goerli #{:STT}
:xdai #{}}
:preview-privacy? true
:fleet :eth.prod
:wallet-set-up-passed? false
:public-key
"0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c"
:keycard-key-uid nil
:installation-id "618ec020-13c8-5505-8aa6-9c5444317e7f"})
(def multiaccounts
{"address" multiaccount})
(defn get-chats [_ _] chats)
(def transport
{"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"
{:resend? nil}
"0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"
{:resend? nil}
"status"
{:resend? nil}})
(def topics
{"0xf8946aac" {:chat-ids #{:discovery-topic}
:last-request 1547319670}})

View File

@ -1,59 +0,0 @@
(ns status-im.multiaccounts.login.flow-test
"The main purpose of these tests is to signal that some steps of the sign in
flow has been changed. Such changes should be reflected in both these tests
and documents which describe the whole \"sign in\" flow."
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.ethereum.core :as ethereum]
[status-im.multiaccounts.login.core :as login.core]
[status-im.multiaccounts.login.data-test :as data]))
(deftest on-password-input-submitted
(testing
"handling :multiaccounts.login.ui/password-input-submitted event"
(let [cofx {:db {:profile/login {:key-uid "key-uid"
:password "password"
:name "user"}}}
efx (login.core/login cofx)]
(testing "Change multiaccount."
(is (= (::login.core/login efx)
["key-uid" "{\"name\":\"user\",\"key-uid\":\"key-uid\"}"
(ethereum/sha3 "password")])))
(testing "start activity indicator"
(is (= (get-in efx [:db :profile/login :processing]) true))))))
(deftest login-success
(testing ":accounts.login.callback/login-success event received."
(let [db {:profile/login {:address "address"
:password "password"}
:profile/profile data/multiaccount}
cofx {:db db}
efx (login.core/multiaccount-login-success cofx)
json-rpc (into #{} (map :method (:json-rpc/call efx)))]
;; TODO: Account is now cleared only after all sign in fx are executed.
;; (testing ":accounts/login cleared."
;; (is (not (contains? new-db :profile/login))))
(testing "Check the rest of effects."
(is (json-rpc "web3_clientVersion"))))))
;;TODO re-enable when keycard is fixed
#_(deftest login
(testing "login with keycard"
(let
[wpk "c56c7ac797c27b3790ce02c2459e9957c5d20d7a2c55320535526ce9e4dcbbef"
epk
"04f43da85ff1c333f3e7277b9ac4df92c9120fbb251f1dede7d41286e8c055acfeb845f6d2654821afca25da119daff9043530b296ee0e28e202ba92ec5842d617"
db
{:keycard
{:profile/profile
{:encryption-public-key epk
:whisper-private-key wpk
:wallet-address "83278851e290d2488b6add2a257259f5741a3b7d"
:whisper-public-key
"0x04491c1272149d7fa668afa45968c9914c0661641ace7dbcbc585c15070257840a0b4b1f71ce66c2147e281e1a44d6231b4731a26f6cc0a49e9616bbc7fc2f1a93"
:whisper-address "b8bec30855ff20c2ddab32282e2b2c8c8baca70d"}}}
result (login.core/login {:db db})]
(is (= (-> result (get :keycard/login-with-keycard) keys count)
3))
(is (= (get-in result [:keycard/login-with-keycard :whisper-private-key wpk])))
(is (= (get-in result [:keycard/login-with-keycard :encryption-public-key epk])))
(is (fn? (get-in result [:keycard/login-with-keycard :on-result]))))))

View File

@ -5,9 +5,24 @@
[native-module.core :as native-module]
[status-im.notifications.core :as notifications]
[utils.re-frame :as rf]
[status-im.utils.keychain.core :as keychain]
[status-im.wallet.core :as wallet]
[status-im2.events :as init]))
[status-im2.common.keychain.events :as keychain]
[status-im2.db :as db]))
(re-frame/reg-fx
::logout
(fn []
(native-module/logout)))
(rf/defn initialize-app-db
[{{:keys [keycard]
:biometric/keys [supported-type]
:network/keys [type]}
:db}]
{:db (assoc db/app-db
:network/type type
:keycard (dissoc keycard :secrets :pin :application-info)
:biometric/supported-type supported-type)})
(rf/defn logout-method
{:events [::logout-method]}
@ -21,12 +36,11 @@
::logout nil
::multiaccounts/webview-debug-changed false
:keychain/clear-user-password key-uid
:profile/get-profiles-overview #(re-frame/dispatch
[:profile/get-profiles-overview-success
%])}
:profile/get-profiles-overview #(rf/dispatch
[:profile/get-profiles-overview-success %])}
(keychain/save-auth-method key-uid auth-method)
(wallet/clear-timeouts)
(init/initialize-app-db))))
(initialize-app-db))))
(rf/defn logout
{:events [:logout :multiaccounts.logout.ui/logout-confirmed
@ -49,17 +63,3 @@
:confirm-button-text (i18n/label :t/logout)
:on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
:on-cancel nil}})
(rf/defn biometric-logout
{:events [:biometric-logout]}
[cofx]
(rf/merge cofx
(logout-method {:auth-method keychain/auth-method-biometric-prepare
:logout? false})
(fn [{:keys [db]}]
{:db (assoc-in db [:profile/login :save-password?] true)})))
(re-frame/reg-fx
::logout
(fn []
(native-module/logout)))

View File

@ -1,86 +1,10 @@
(ns status-im.multiaccounts.recover.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.events :as bottom-sheet]
(:require [re-frame.core :as re-frame]
[status-im2.constants :as constants]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic]
[utils.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.create.core :as multiaccounts.create]
[native-module.core :as native-module]
[status-im.popover.core :as popover]
[utils.re-frame :as rf]
[status-im.utils.types :as types]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.security.core :as security]))
(defn existing-account?
[multiaccounts key-uid]
{:pre [(not (nil? key-uid))]}
(contains? multiaccounts key-uid))
(defn check-phrase-warnings
[recovery-phrase]
(cond (string/blank? recovery-phrase) :t/required-field))
(rf/defn set-phrase
{:events [:multiaccounts.recover/passphrase-input-changed]}
[{:keys [db]} masked-recovery-phrase]
(let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)]
{:db (update db
:intro-wizard assoc
:passphrase (string/lower-case recovery-phrase)
:passphrase-error nil
:next-button-disabled? (or (empty? recovery-phrase)
(not (mnemonic/valid-length? recovery-phrase))))}))
(rf/defn validate-phrase-for-warnings
[{:keys [db]}]
(let [recovery-phrase (get-in db [:intro-wizard :passphrase])]
{:db (update db
:intro-wizard assoc
:passphrase-error (check-phrase-warnings recovery-phrase))}))
(rf/defn on-store-multiaccount-success
{:events [::store-multiaccount-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx} result password]
(let [{:keys [error]} (types/json->clj result)]
(if error
{:utils/show-popup {:title (i18n/label :t/multiaccount-exists-title)
:content (i18n/label :t/multiaccount-exists-title)
:on-dismiss #(re-frame/dispatch [:pop-to-root :multiaccounts])}}
(let [{:keys [key-uid] :as multiaccount} (get-in db [:intro-wizard :root-key])
keycard-multiaccount? (boolean (get-in db
[:profile/profiles-overview key-uid
:keycard-pairing]))]
(if keycard-multiaccount?
;; trying to recover multiaccount created with keycard
{:db (-> db
(update :intro-wizard assoc
:processing? false
:passphrase-error :recover-keycard-multiaccount-not-supported)
(update :intro-wizard
dissoc
:passphrase-valid?))}
(let [multiaccount (assoc multiaccount :derived (get-in db [:intro-wizard :derived]))]
(multiaccounts.create/on-multiaccount-created cofx
multiaccount
password
{})))))))
(rf/defn store-multiaccount
{:events [::recover-multiaccount-confirmed]}
[{:keys [db]} password]
(let [{:keys [root-key]} (:intro-wizard db)
{:keys [id key-uid]} root-key
callback #(re-frame/dispatch [::store-multiaccount-success % password])
hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
{:db (assoc-in db [:intro-wizard :processing?] true)
::multiaccounts.create/store-multiaccount [id key-uid hashed-password callback]}))
[taoensso.timbre :as log]))
(re-frame/reg-fx
::import-multiaccount
@ -110,68 +34,3 @@
(update derived-data constants/path-whisper-keyword assoc :name name)]
(re-frame/dispatch [success-event root-data derived-data-extended]))))))))))))
(rf/defn show-existing-multiaccount-alert
[_ key-uid]
{:utils/show-confirmation
{:title (i18n/label :t/multiaccount-exists-title)
:content (i18n/label :t/multiaccount-exists-content)
:confirm-button-text (i18n/label :t/unlock)
:on-accept #(do
(re-frame/dispatch [:pop-to-root :multiaccounts])
(re-frame/dispatch
[:profile/profile-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}})
(rf/defn on-import-multiaccount-success
{:events [::import-multiaccount-success]}
[{:keys [db] :as cofx} {:keys [key-uid] :as root-data} derived-data]
(let [multiaccounts (:profile/profiles-overview db)]
(rf/merge
cofx
{:db (update db
:intro-wizard
assoc
:root-key root-data
:derived derived-data
:step :recovery-success)}
(when (existing-account? multiaccounts key-uid)
(show-existing-multiaccount-alert key-uid))
(navigation/navigate-to :recover-multiaccount-success nil))))
(rf/defn enter-phrase-pressed
{:events [::enter-phrase-pressed]}
[{:keys [db] :as cofx}]
(rf/merge
cofx
{:db (-> db
(assoc :intro-wizard
{:step :enter-phrase
:recovering? true
:next-button-disabled? true
:weak-password? true
:encrypt-with-password? true
:forward-action :multiaccounts.recover/enter-phrase-next-pressed}
:recovered-account? true)
(update :keycard dissoc :flow))}
(bottom-sheet/hide-bottom-sheet-old)
(navigation/navigate-to :recover-multiaccount-enter-phrase nil)))
(rf/defn proceed-to-import-mnemonic
{:events [:multiaccounts.recover/phrase-validated]}
[{:keys [db] :as cofx} phrase-warnings]
(let [{:keys [password passphrase]} (:intro-wizard db)]
(if-not (string/blank? (:error (types/json->clj phrase-warnings)))
(popover/show-popover cofx {:view :custom-seed-phrase})
(when (mnemonic/valid-length? passphrase)
{::import-multiaccount {:passphrase (mnemonic/sanitize-passphrase passphrase)
:password (when password
(security/safe-unmask-data password))
:success-event ::import-multiaccount-success}}))))
(rf/defn seed-phrase-next-pressed
{:events [:multiaccounts.recover/enter-phrase-next-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [passphrase]} (:intro-wizard db)]
{::multiaccounts/validate-mnemonic [passphrase
#(re-frame/dispatch [:multiaccounts.recover/phrase-validated
%])]}))

View File

@ -1,85 +0,0 @@
(ns status-im.multiaccounts.recover.core-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.multiaccounts.recover.core :as models]
[utils.security.core :as security]))
;;;; helpers
(deftest check-phrase-warnings
(is (= :t/required-field (models/check-phrase-warnings ""))))
;;;; handlers
(deftest set-phrase
(is
(= {:db {:intro-wizard
{:passphrase
"game buzz method pretty olympic fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase
{:db {}}
(security/mask-data
"game buzz method pretty olympic fat quit display velvet unveil marine crater"))))
(is
(= {:db {:intro-wizard
{:passphrase
"game buzz method pretty olympic fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase
{:db {}}
(security/mask-data
"Game buzz method pretty Olympic fat quit DISPLAY velvet unveil marine crater"))))
(is
(= {:db {:intro-wizard {:passphrase
"game buzz method pretty zeus fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase {:db {}}
(security/mask-data
"game buzz method pretty zeus fat quit display velvet unveil marine crater"))))
(is
(=
{:db {:intro-wizard
{:passphrase
" game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater "
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase
{:db {}}
(security/mask-data
" game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater "))))
(is
(= {:db {:intro-wizard {:passphrase
"game buzz method pretty 1234 fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase
{:db {}}
(security/mask-data
"game buzz method pretty 1234 fat quit display velvet unveil marine crater")))))
(deftest store-multiaccount
(let [new-cofx (models/store-multiaccount
{:db {:intro-wizard
{:passphrase
"game buzz method pretty zeus fat quit display velvet unveil marine crater"}}}
(security/mask-data "thisisapaswoord"))]
(is (::multiaccounts.create/store-multiaccount new-cofx))))
(deftest on-import-multiaccount-success
(testing "importing a new multiaccount"
(let [res (models/on-import-multiaccount-success
{:db {:profile/profiles-overview {:acc1 {}}}}
{:key-uid :acc2}
nil)]
(is (nil? (:utils/show-confirmation res)))))
(testing "importing an existing multiaccount"
(let [res (models/on-import-multiaccount-success
{:db {:profile/profiles-overview {:acc1 {}}}}
{:key-uid :acc1}
nil)]
(is (contains? res :utils/show-confirmation)))))

View File

@ -5,7 +5,6 @@
[native-module.core :as native-module]
[status-im.popover.core :as popover]
[utils.re-frame :as rf]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.types :as types]
[utils.security.core :as security]))
@ -35,19 +34,13 @@
(rf/defn password-reset-success
{:events [::password-reset-success]}
[{:keys [db] :as cofx}]
(let [{:keys [key-uid]} (:profile/profile db)
auth-method (get db :auth-method keychain/auth-method-none)
new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])]
(rf/merge cofx
{:db (dissoc
db
:multiaccount/reset-password-form-vals
:multiaccount/reset-password-errors
:multiaccount/reset-password-next-enabled?
:multiaccount/resetting-password?)}
;; update password in keychain if biometrics are enabled
(when (= auth-method keychain/auth-method-biometric)
(keychain/save-user-password key-uid new-password)))))
(rf/merge cofx
{:db (dissoc
db
:multiaccount/reset-password-form-vals
:multiaccount/reset-password-errors
:multiaccount/reset-password-next-enabled?
:multiaccount/resetting-password?)}))
(defn change-db-password-cb
[res]

View File

@ -88,7 +88,7 @@
(assoc-in [:syncing :pairing-status] :connected)
received-account?
(assoc-in [:syncing :profile/profile] multiaccount-data)
(assoc-in [:syncing :profile] multiaccount-data)
error-on-pairing?
(assoc-in [:syncing :pairing-status] :error)
@ -105,7 +105,7 @@
{:dispatch [:syncing/clear-states]}
(and completed-pairing? receiver?)
{:dispatch [:multiaccounts.login/local-paired-user]}
{:dispatch [:profile.login/local-paired-user]}
(and error-on-pairing? (some? error))
{:dispatch [:toasts/upsert

View File

@ -1,97 +0,0 @@
(ns status-im.ui.screens.biometric.views
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[utils.i18n :as i18n]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]))
(defn get-supported-biometric-auth
[]
@(re-frame/subscribe [:supported-biometric-auth]))
(defn get-bio-type-label
[]
(biometric/get-label (get-supported-biometric-auth)))
(defn biometric-popover
[{:keys [title-label description-label description-text
ok-button-label cancel-button-label on-cancel on-confirm]}]
(let [supported-biometric-auth (get-supported-biometric-auth)
bio-type-label (get-bio-type-label)]
[react/view
{:margin-top 24
:align-items :center
:padding-horizontal 24}
[react/view
{:width 32
:height 32
:background-color colors/blue-light
:border-radius 16
:align-items :center
:justify-content :center}
[icons/icon
(if (= supported-biometric-auth :FaceID)
:main-icons/faceid
:main-icons/print)
{:color colors/blue}]]
[react/text
{:style {:typography :title-bold
:margin-top 16}}
(str (i18n/label title-label {:bio-type-label bio-type-label}))]
(vec
(concat
[react/nested-text
{:style {:margin-top 8
:color colors/gray
:text-align :center}}]
(if description-label
[(i18n/label description-label {:bio-type-label bio-type-label})]
description-text)))
[react/view {:padding-vertical 16}
[react/view {:padding-vertical 8}
[quo/button {:on-press #(re-frame/dispatch [on-confirm])}
(i18n/label ok-button-label
{:bio-type-label bio-type-label})]]
[quo/button
{:type :secondary
:on-press #(re-frame/dispatch [(or on-cancel :hide-popover)])}
(or cancel-button-label
(i18n/label :t/cancel))]]]))
(defn disable-password-saving-popover
[]
(let [bio-label-type (get-bio-type-label)]
[biometric-popover
{:title-label :t/biometric-disable-password-title
:ok-button-label :t/continue
:on-confirm :biometric/disable
:description-text
[[{:style {:color colors/gray}}
(i18n/label :t/biometric-disable-password-description)]
[{}
(i18n/label :t/biometric-disable-bioauth
{:bio-type-label bio-label-type})]]}]))
(defn enable-biometric-popover
[]
[biometric-popover
{:title-label :t/biometric-secure-with
:description-label :t/to-enable-biometric
:ok-button-label :t/biometric-enable-button
:on-confirm :biometric-logout}])
(defn secure-with-biometric-popover
[]
(let [keycard-account? @(re-frame/subscribe
[:multiaccounts.login/keycard-account?])]
[biometric-popover
{:title-label :t/biometric-secure-with
:ok-button-label :t/biometric-enable-button
:on-confirm :biometric/enable
:description-label (if keycard-account?
:t/biometric-enable-keycard
:t/biometric-enable)}]))

View File

@ -94,9 +94,10 @@
[react/view
{:style {:flex-direction :row}}
[checkbox/checkbox
{:checked? save-password?
:style {:margin-right 10}
:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
{:checked? save-password?
:style {:margin-right 10}}]
;; should be reimplemented
;;:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
[react/text (i18n/label :t/keycard-dont-ask-card)]])))
(defn bezier-easing

View File

@ -9,7 +9,6 @@
[utils.i18n :as i18n]
[status-im.keycard.login :as keycard.login]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.react-native.resources :as resources]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
@ -444,7 +443,6 @@
{:events [:multiaccounts.create.ui/get-new-key]}
[{:keys [db] :as cofx}]
(rf/merge cofx
(multiaccounts.create/prepare-intro-wizard)
(bottom-sheet/hide-bottom-sheet-old)
(navigation/navigate-to :get-your-keys nil)))

View File

@ -6,7 +6,6 @@
[reagent.core :as reagent]
[status-im.ui.components.animation :as anim]
[status-im.ui.components.react :as react]
[status-im.ui.screens.biometric.views :as biometric]
[status-im.ui.screens.keycard.frozen-card.view :as frozen-card]
[status-im.ui.screens.keycard.views :as keycard.views]
[status-im.ui.screens.profile.user.views :as profile.user]
@ -142,15 +141,6 @@
(= :share-chat-key view)
[profile.user/share-chat-key]
(= :enable-biometric view)
[biometric/enable-biometric-popover]
(= :secure-with-biometric view)
[biometric/secure-with-biometric-popover]
(= :disable-password-saving view)
[biometric/disable-password-saving-popover]
(= :transaction-data view)
[signing/transaction-data]

View File

@ -3,7 +3,6 @@
[re-frame.core :as re-frame]
[status-im2.constants :as constants]
[utils.i18n :as i18n]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.reset-password.core :as reset-password]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.ui.components.common.common :as components.common]
@ -34,9 +33,7 @@
profile-pictures-visibility]}
[:profile/profile]
has-picture [:profile/has-picture]
supported-biometric-auth [:supported-biometric-auth]
keycard? [:keycard-multiaccount?]
auth-method [:auth-method]
profile-pictures-show-to [:multiaccount/profile-pictures-show-to]]
[react/scroll-view {:padding-vertical 8}
[quo/list-header (i18n/label :t/security)]
@ -48,18 +45,6 @@
:chevron (boolean mnemonic)
:accessory (when mnemonic [components.common/counter {:size 22} 1])
:on-press #(re-frame/dispatch [:navigate-to :backup-seed])}]
(when supported-biometric-auth
[quo/list-item
{:size :small
:title (str (i18n/label :t/lock-app-with)
" "
(biometric/get-label supported-biometric-auth))
:active (= auth-method "biometric")
:accessibility-label :biometric-auth-settings-switch
:accessory :switch
:on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched
((complement boolean)
(= auth-method "biometric"))])}])
[separator]
[quo/list-header (i18n/label :t/privacy)]
[quo/list-item

View File

@ -58,7 +58,6 @@
[status-im.ui.screens.rpc-usage-info :as rpc-usage-info]
[status-im.ui.screens.stickers.views :as stickers]
[status-im.ui.screens.sync-settings.views :as sync-settings]
[status-im.ui.screens.terms-of-service.views :as terms-of-service]
[status-im.ui.screens.wakuv2-settings.edit-node.views :as edit-wakuv2-node]
[status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings]
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
@ -349,9 +348,6 @@
:options {:topBar {:title {:text (i18n/label :t/principles)}}
:insets {:top? true}}
:component about-app/principles}
{:name :force-accept-tos
:options {:insets {:top? true}}
:component terms-of-service/force-accept-tos}
{:name :manage-dapps-permissions
;;TODO dynamic title
:options {:insets {:top? true}}

View File

@ -1,112 +0,0 @@
(ns status-im.ui.screens.terms-of-service.views
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.design-system.typography :as typography]
[re-frame.core :as re-frame]
[status-im2.constants :refer [docs-link]]
[utils.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.toolbar :as toolbar])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn principles-item
[]
[react/nested-text {}
(i18n/label :t/wc-new-tos-based-on-principles-prefix)
[{:style (merge {:color colors/blue}
typography/font-medium)
:on-press #(re-frame/dispatch [:open-modal :principles])}
" "
(i18n/label :t/principles)]])
(def changes
[[principles-item]
:wc-how-to-use-status-app
:wc-brand-guide
:wc-disclaimer
:wc-dispute])
(defn change-list-item
[label]
[react/view
{:flex-direction :row
:align-items :center
:margin-horizontal (:base spacing/spacing)
:margin-vertical (:tiny spacing/spacing)}
[icons/icon :main-icons/checkmark-circle
{:color colors/blue
:container-style {:margin-top 1.2
:margin-right (:tiny spacing/spacing)}}]
[react/view {:style {:padding-right (:xx-large spacing/spacing)}}
(if (keyword? label)
[react/text (i18n/label label)]
label)]])
(defview force-accept-tos
[]
(letsubs [next-root [:tos-accept-next-root]]
[react/scroll-view
[react/view
{:style (merge {:align-items :center}
(:x-large spacing/padding-horizontal))}
[react/image
{:source (resources/get-image :status-logo)
:style {:margin-vertical (:base spacing/spacing)
:width 32
:height 32}}]
[quo/text
{:size :x-large
:align :center
:weight :bold
:style {:margin-bottom (:base spacing/spacing)}}
(i18n/label :t/updates-to-tos)]
[quo/text
{:color :secondary
:align :center}
(i18n/label :t/updates-to-tos-desc)]]
[quo/separator {:style {:margin-top (:base spacing/spacing)}}]
[quo/list-item
{:title [quo/text
{:color :link
:weight :medium}
(i18n/label :t/terms-of-service)]
:accessibility-label :tos
:chevron true
:on-press #(re-frame/dispatch [:open-modal :terms-of-service])}]
[quo/separator]
[quo/list-header (i18n/label :t/what-changed)]
(for [c changes]
^{:key c}
[change-list-item c])
[quo/separator {:style {:margin-vertical (:base spacing/spacing)}}]
[react/view {:style (:base spacing/padding-horizontal)}
[quo/text {:weight :medium} (i18n/label :t/status-is-open-source)]
[quo/text {:color :secondary} (i18n/label :t/build-yourself)]
[quo/text
{:color :link
:weight :medium
:on-press #(.openURL ^js react/linking docs-link)}
docs-link]]
[quo/separator {:style {:margin-vertical (:base spacing/spacing)}}]
[toolbar/toolbar
{:size :large
:center
[react/view {:padding-horizontal 8}
[quo/button
{:type :primary
:on-press #(do
(re-frame/dispatch [:hide-terms-of-services-opt-in-screen])
(re-frame/dispatch [:init-root next-root]))}
(i18n/label :t/accept-and-continue)]]}]]))
(comment
(re-frame/dispatch [:navigate-to :force-accept-tos]))

View File

@ -1,213 +1,45 @@
(ns status-im.utils.keychain.core
(:require ["react-native-keychain" :as react-native-keychain]
[clojure.string :as string]
(:require [react-native.keychain :as keychain]
[re-frame.core :as re-frame]
[native-module.core :as native-module]
[utils.re-frame :as rf]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]
[utils.security.core :as security]))
[oops.core :as oops]))
(defn- check-conditions
[callback & checks]
(if (= (count checks) 0)
(callback true)
(let [current-check-fn (first checks)
process-check-result (fn [callback-success callback-fail]
(fn [current-check-passed?]
(if current-check-passed?
(callback-success)
(callback-fail))))]
(current-check-fn (process-check-result
#(apply (partial check-conditions callback) (rest checks))
#(callback false))))))
;; ********************************************************************************
;; Storing / Retrieving a user password to/from Keychain
;; ********************************************************************************
;;
;; We are using set/get/reset internet credentials there because they are bound
;; to an address (`server`) property.
(defn enum-val
[enum-name value-name]
(get-in (js->clj react-native-keychain) [enum-name value-name]))
;; We need a more strict access mode for keychain entries that save user password.
;; iOS
;; see this article for more details:
;; https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility?language=objc
(def keychain-restricted-availability
;; From Apple's documentation:
;; > The kSecAttrAccessible attribute enables you to control item availability
;; > relative to the lock state of the device.
;; > It also lets you specify eligibility for restoration to a new device.
;; > If the attribute ends with the string ThisDeviceOnly,
;; > the item can be restored to the same device that created a backup,
;; > but it isnt migrated when restoring another devices backup data.
;; > ...
;; > For extremely sensitive data
;; > THAT YOU NEVER WANT STORED IN iCloud,
;; > you might choose kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly.
;; That is exactly what we use there.
;; Note that the password won't be stored if the device isn't locked by a passcode.
#js {:accessible (enum-val "ACCESSIBLE" "WHEN_PASSCODE_SET_THIS_DEVICE_ONLY")})
(def keychain-secure-hardware
;; (Android) Requires storing the encryption key for the entry in secure hardware
;; or StrongBox (see https://developer.android.com/training/articles/keystore#ExtractionPrevention)
"SECURE_HARDWARE")
;; These helpers check if the device is okay to use for password storage
;; They resolve callback with `true` if the check is passed, with `false` otherwise.
;; Android only
(defn- device-not-rooted?
[callback]
(native-module/rooted-device? (fn [rooted?] (callback (not rooted?)))))
;; Android only
(defn- secure-hardware-available?
[callback]
(-> (.getSecurityLevel react-native-keychain)
(.then (fn [level] (callback (= level keychain-secure-hardware))))))
;; iOS only
(defn- device-encrypted?
[callback]
(-> (.canImplyAuthentication
react-native-keychain
(clj->js
{:authenticationType
(enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")}))
(.then callback)))
(def auth-method-biometric "biometric")
(def auth-method-biometric-prepare "biometric-prepare")
(def auth-method-none "none")
(defn- whisper-key-name
[address]
(str address "-whisper"))
(defn can-save-user-password?
[callback]
(log/debug "[keychain] can-save-user-password?")
(cond
platform/ios?
(check-conditions callback device-encrypted?)
platform/android?
(check-conditions callback secure-hardware-available? device-not-rooted?)
:else
(callback false)))
(defn save-credentials
"Stores the credentials for the address to the Keychain"
[server username password callback]
(log/debug "[keychain] save-credentials")
(-> (.setInternetCredentials react-native-keychain
(string/lower-case server)
username
password
keychain-secure-hardware
keychain-restricted-availability)
(.then callback)))
(defn get-credentials
"Gets the credentials for a specified server from the Keychain"
[server callback]
(log/debug "[keychain] get-credentials")
(-> (.getInternetCredentials react-native-keychain (string/lower-case server))
(.then callback)))
(def auth-method-password "password")
(def auth-method-biometric "biometric")
(def auth-method-biometric-prepare "biometric-prepare")
(def auth-method-none "none")
(re-frame/reg-fx
:keychain/get-auth-method
(fn [[key-uid callback]]
(can-save-user-password?
(fn [can-save?]
(if can-save?
(get-credentials (str key-uid "-auth")
#(callback (if %
(.-password ^js %)
auth-method-none)))
(callback nil))))))
(re-frame/reg-fx
:keychain/get-user-password
(fn [[key-uid callback]]
(get-credentials key-uid #(if % (callback (security/mask-data (.-password ^js %))) (callback nil)))))
(re-frame/reg-fx
:keychain/get-keycard-keys
(fn [[key-uid callback]]
(get-credentials
(keychain/get-credentials
key-uid
(fn [^js encryption-key-data]
(fn [encryption-key-data]
(if encryption-key-data
(get-credentials
(keychain/get-credentials
(whisper-key-name key-uid)
(fn [^js whisper-key-data]
(fn [whisper-key-data]
(if whisper-key-data
(callback [(.-password encryption-key-data)
(.-password whisper-key-data)])
(callback [(oops/oget encryption-key-data "password")
(oops/oget whisper-key-data "password")])
(callback nil))))
(callback nil))))))
(defn save-user-password!
[key-uid password]
(save-credentials
key-uid
key-uid
(security/safe-unmask-data password)
#(when-not %
(log/error
(str "Error while saving password."
" "
"The app will continue to work normally, "
"but you will have to login again next time you launch it.")))))
(defn save-auth-method!
[key-uid method]
(save-credentials
(str key-uid "-auth")
key-uid
method
#(when-not %
(log/error
(str "Error while saving auth method."
" "
"The app will continue to work normally, "
"but you will have to login again next time you launch it.")))))
(re-frame/reg-fx
:keychain/save-user-password
(fn [[key-uid masked-password]]
(save-user-password! key-uid masked-password)))
(re-frame/reg-fx
:keychain/save-auth-method
(fn [[key-uid method]]
(log/debug "[keychain] :keychain/save-auth-method"
"key-uid"
key-uid
"method"
method)
(when-not (empty? key-uid) ; key-uid may be nil after restore from local pairing
(save-auth-method! key-uid method))))
(re-frame/reg-fx
:keychain/save-keycard-keys
(fn [[key-uid encryption-public-key whisper-private-key]]
(save-credentials
(keychain/save-credentials
key-uid
key-uid
encryption-public-key
#(when-not %
(log/error
(str "Error while saving encryption-public-key"))))
(save-credentials
(keychain/save-credentials
(whisper-key-name key-uid)
key-uid
whisper-private-key
@ -215,19 +47,6 @@
(log/error
(str "Error while saving whisper-private-key"))))))
(re-frame/reg-fx
:keychain/clear-user-password
(fn [key-uid]
(-> (.resetInternetCredentials react-native-keychain (string/lower-case key-uid))
(.then #(when-not % (log/error (str "Error while clearing saved password.")))))))
(rf/defn get-user-password
[_ key-uid]
{:keychain/get-user-password
[key-uid
#(re-frame/dispatch
[:multiaccounts.login.callback/get-user-password-success % key-uid])]})
(rf/defn get-keycard-keys
[_ key-uid]
{:keychain/get-keycard-keys
@ -235,16 +54,8 @@
#(re-frame/dispatch
[:multiaccounts.login.callback/get-keycard-keys-success key-uid %])]})
(rf/defn save-user-password
[_ key-uid password]
{:keychain/save-user-password [key-uid password]})
(rf/defn save-keycard-keys
[_ key-uid encryption-public-key whisper-private-key]
{:keychain/save-keycard-keys [key-uid
encryption-public-key
whisper-private-key]})
(rf/defn save-auth-method
[{:keys [db]} key-uid method]
{:db (assoc db :auth-method method)
:keychain/save-auth-method [key-uid method]})

View File

@ -36,16 +36,21 @@
(fn [json callback]
(callback (.multiAccountStoreDerivedAccounts native-status json)))
:getNodeConfig (fn [] (types/clj->json {:WakuV2Config ""}))
:backupDisabledDataDir (fn [] "./smth")
:keystoreDir (fn [] "")
:logFileDirectory (fn [] "./smth")
:clearCookies identity
:clearStorageAPIs identity
:setBlankPreviewFlag identity
:callPrivateRPC
(fn [payload callback]
(callback (.callPrivateRPC native-status payload)))
:createAccountAndLogin (fn [request] (.createAccountAndLogin native-status request))
:saveAccountAndLogin
(fn [multiaccount-data password settings config accounts-data]
(.saveAccountAndLogin native-status multiaccount-data password settings config accounts-data))

View File

@ -333,6 +333,20 @@
(update-in [:wallet :accounts] dissoc deleted-address))}
(navigation/pop-to-root :shell-stack))))
(re-frame/reg-fx
:key-storage/delete-imported-key
(fn [{:keys [key-uid address password on-success on-error]}]
(let [hashed-pass (ethereum/sha3 (security/safe-unmask-data password))]
(native-module/delete-imported-key
key-uid
(string/lower-case (subs address 2))
hashed-pass
(fn [result]
(let [{:keys [error]} (types/json->clj result)]
(if-not (string/blank? error)
(on-error error)
(on-success))))))))
(rf/defn delete-account-key
{:events [:wallet.accounts/delete-key]}
[{:keys [db] :as cofx} account password on-error]

View File

@ -0,0 +1,76 @@
(ns status-im2.common.biometric.events
(:require [react-native.platform :as platform]
[react-native.touch-id :as touch-id]
[native-module.core :as native-module]
[status-im2.common.keychain.events :as keychain]
[re-frame.core :as re-frame]
[utils.re-frame :as rf]
[utils.i18n :as i18n]))
(def android-device-blacklisted?
(= (:brand (native-module/get-device-model-info)) "bannedbrand"))
(defn get-supported-type
[callback]
(cond platform/ios? (touch-id/get-supported-type callback)
platform/android? (if android-device-blacklisted?
(callback nil)
(touch-id/get-supported-type callback))
:else (callback nil)))
(defn get-label-by-type
[biometric-type]
(case biometric-type
:fingerprint (i18n/label :t/biometric-fingerprint)
:FaceID (i18n/label :t/biometric-faceid)
(i18n/label :t/biometric-touchid)))
(re-frame/reg-fx
:biometric/get-supported-biometric-type
(fn []
;;NOTE: if we can't save user password, we can't use biometric
(keychain/can-save-user-password?
(fn [can-save?]
(when can-save?
(get-supported-type #(rf/dispatch [:biometric/get-supported-biometric-type-success %])))))))
(rf/defn get-supported-biometric-auth-success
{:events [:biometric/get-supported-biometric-type-success]}
[{:keys [db]} supported-type]
{:db (assoc db :biometric/supported-type supported-type)})
(rf/defn show-message
[_ code]
(let [content (if (#{"NOT_AVAILABLE" "NOT_ENROLLED"} code)
(i18n/label :t/grant-face-id-permissions)
(when-not (or (= code "USER_CANCELED") (= code "USER_FALLBACK"))
(i18n/label :t/biometric-auth-error {:code code})))]
(when content
{:utils/show-popup
{:title (i18n/label :t/biometric-auth-login-error-title)
:content content}})))
(re-frame/reg-fx
:biometric/authenticate
(fn [options]
(touch-id/authenticate
(merge
{:reason (i18n/label :t/biometric-auth-reason-login)
:options (merge
{:unifiedErrors true}
(when platform/ios?
{:passcodeFallback false
:fallbackLabel (i18n/label :t/biometric-auth-login-ios-fallback-label)})
(when platform/android?
{:title (i18n/label :t/biometric-auth-android-title)
:imageColor :blue
:imageErrorColor :red
:sensorDescription (i18n/label :t/biometric-auth-android-sensor-desc)
:sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc)
:cancelText (i18n/label :cancel)}))}
options))))
(rf/defn authenticate
{:events [:biometric/authenticate]}
[_ opts]
{:biometric/authenticate opts})

View File

@ -0,0 +1,110 @@
(ns status-im2.common.keychain.events
(:require [taoensso.timbre :as log]
[react-native.platform :as platform]
[react-native.keychain :as keychain]
[re-frame.core :as re-frame]
[utils.re-frame :as rf]
[oops.core :as oops]
[native-module.core :as native-module]
[utils.security.core :as security]))
(defn- check-conditions
[callback & checks]
(if (= (count checks) 0)
(callback true)
(let [current-check-fn (first checks)
process-check-result (fn [callback-success callback-fail]
(fn [current-check-passed?]
(if current-check-passed?
(callback-success)
(callback-fail))))]
(current-check-fn (process-check-result
#(apply (partial check-conditions callback) (rest checks))
#(callback false))))))
;; These helpers check if the device is okay to use for password storage
;; They resolve callback with `true` if the check is passed, with `false` otherwise.
;; Android only
(defn- device-not-rooted?
[callback]
(native-module/rooted-device? (fn [rooted?] (callback (not rooted?)))))
(defn can-save-user-password?
[callback]
(log/debug "[keychain] can-save-user-password?")
(cond
platform/ios?
(check-conditions callback keychain/device-encrypted?)
platform/android?
(check-conditions callback keychain/secure-hardware-available? device-not-rooted?)
:else
(callback false)))
(def auth-method-biometric "biometric")
(def auth-method-biometric-prepare "biometric-prepare")
(def auth-method-none "none")
(defn save-auth-method!
[key-uid method]
(keychain/save-credentials
(str key-uid "-auth")
key-uid
method
#(when-not %
(log/error
(str "Error while saving auth method."
" "
"The app will continue to work normally, "
"but you will have to login again next time you launch it.")))))
(re-frame/reg-fx
:keychain/save-auth-method
(fn [[key-uid method]]
(when-not (empty? key-uid) ; key-uid may be nil after restore from local pairing
(save-auth-method! key-uid method))))
(rf/defn save-auth-method
[{:keys [db]} key-uid method]
{:db (assoc db :auth-method method)
:keychain/save-auth-method [key-uid method]})
(re-frame/reg-fx
:keychain/get-auth-method
(fn [[key-uid callback]]
(can-save-user-password?
(fn [can-save?]
(if can-save?
(keychain/get-credentials
(str key-uid "-auth")
#(callback (if % (oops/oget % "password") auth-method-none)))
(callback nil))))))
(defn save-user-password!
[key-uid password]
(keychain/save-credentials key-uid key-uid (security/safe-unmask-data password) #()))
(re-frame/reg-fx
:keychain/get-user-password
(fn [[key-uid callback]]
(keychain/get-credentials
key-uid
#(if % (callback (security/mask-data (oops/oget % "password"))) (callback nil)))))
(rf/defn get-user-password
[_ key-uid callback]
{:keychain/get-user-password [key-uid callback]})
(re-frame/reg-fx
:keychain/clear-user-password
(fn [key-uid]
(keychain/reset-credentials key-uid)))
(re-frame/reg-fx
:keychain/save-password-and-auth-method
(fn [{:keys [key-uid masked-password on-success on-error]}]
(-> (save-user-password! key-uid masked-password)
(.then #(save-auth-method! key-uid auth-method-biometric))
(.then #(when on-success (on-success)))
(.catch #(when on-error (on-error %))))))

View File

@ -5,13 +5,13 @@
[react-native.safe-area :as safe-area]
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
[status-im2.contexts.onboarding.common.navigation-bar.view :as navigation-bar]
[status-im.multiaccounts.biometric.core :as biometric]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.common.resources :as resources]
[status-im2.common.parallax.view :as parallax]
[status-im2.contexts.onboarding.common.background.view :as background]
[status-im2.common.parallax.whitelist :as whitelist]))
[status-im2.common.parallax.whitelist :as whitelist]
[status-im2.common.biometric.events :as biometric]))
(defn page-title
[]
@ -24,9 +24,9 @@
(defn enable-biometrics-buttons
[insets]
(let [supported-biometric (rf/sub [:supported-biometric-auth])
bio-type-label (biometric/get-label supported-biometric)
profile-color (:color (rf/sub [:onboarding-2/profile]))]
(let [supported-biometric-type (rf/sub [:biometric/supported-type])
bio-type-label (biometric/get-label-by-type supported-biometric-type)
profile-color (:color (rf/sub [:onboarding-2/profile]))]
[rn/view {:style (style/buttons insets)}
[quo/button
{:accessibility-label :enable-biometrics-button

View File

@ -1,21 +1,15 @@
(ns status-im2.contexts.onboarding.events
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[status-im.utils.types :as types]
[status-im2.config :as config]
[status-im2.constants :as constants]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(re-frame/reg-fx
:multiaccount/create-account-and-login
(fn [request]
(native-module/create-account-and-login request)))
(:require [native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.utils.types :as types]
[status-im2.constants :as constants]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]
[status-im2.contexts.profile.create.events :as profile.create]
[status-im2.contexts.profile.recover.events :as profile.recover]
[status-im2.common.biometric.events :as biometric]))
(re-frame/reg-fx
:multiaccount/validate-mnemonic
@ -28,11 +22,6 @@
(when on-error (on-error error))
(on-success mnemonic keyUID)))))))
(re-frame/reg-fx
:multiaccount/restore-account-and-login
(fn [request]
(native-module/restore-account-and-login request)))
(rf/defn profile-data-set
{:events [:onboarding-2/profile-data-set]}
[{:keys [db]} onboarding-data]
@ -42,82 +31,34 @@
(rf/defn enable-biometrics
{:events [:onboarding-2/enable-biometrics]}
[_]
{:biometric-auth/authenticate [#(rf/dispatch [:onboarding-2/biometrics-done %]) {}]})
(rf/defn show-biometrics-message
[cofx bioauth-message bioauth-code]
(let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code)
(i18n/label :t/grant-face-id-permissions))
bioauth-message)]
(when content
{:utils/show-popup
{:title (i18n/label :t/biometric-auth-login-error-title)
:content content}})))
{:biometric/authenticate {:on-success #(rf/dispatch [:onboarding-2/biometrics-done])
:on-fail #(rf/dispatch [:onboarding-2/biometrics-fail %])}})
(rf/defn biometrics-done
{:events [:onboarding-2/biometrics-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(if bioauth-success
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
:dispatch [:onboarding-2/create-account-and-login]}
(show-biometrics-message cofx bioauth-message bioauth-code)))
[{:keys [db]}]
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
:dispatch [:onboarding-2/create-account-and-login]})
(defn strip-file-prefix
[path]
(when path
(string/replace-first path "file://" "")))
(rf/defn biometrics-fail
{:events [:onboarding-2/biometrics-fail]}
[cofx code]
(biometric/show-message cofx code))
(rf/defn create-account-and-login
{:events [:onboarding-2/create-account-and-login]}
[{:keys [db]}]
(let [{:keys [display-name
seed-phrase
password
image-path
color]} (:onboarding-2/profile db)
log-enabled? (boolean (not-empty config/log-level))
effect (if seed-phrase
:multiaccount/restore-account-and-login
:multiaccount/create-account-and-login)
request {:displayName display-name
:deviceName (native-module/get-installation-name)
:password (ethereum/sha3 (security/safe-unmask-data
password))
:mnemonic (when seed-phrase
(security/safe-unmask-data seed-phrase))
:imagePath (strip-file-prefix image-path)
:customizationColor color
:backupDisabledDataDir (native-module/backup-disabled-data-dir)
:rootKeystoreDir (native-module/keystore-dir)
;; Temporary fix until https://github.com/status-im/status-go/issues/3024 is
;; resolved
:wakuV2Nameserver "1.1.1.1"
:logLevel (when log-enabled? config/log-level)
:logEnabled log-enabled?
:logFilePath (native-module/log-file-directory)
:openseaAPIKey config/opensea-api-key
:verifyTransactionURL config/verify-transaction-url
:verifyENSURL config/verify-ens-url
:verifyENSContractAddress config/verify-ens-contract-address
:verifyTransactionChainID config/verify-transaction-chain-id
:upstreamConfig config/default-network-rpc-url
:networkId config/default-network-id
:poktToken config/POKT_TOKEN
:infuraToken config/INFURA_TOKEN
:alchemyOptimismMainnetToken config/ALCHEMY_OPTIMISM_MAINNET_TOKEN
:alchemyOptimismGoerliToken config/ALCHEMY_OPTIMISM_GOERLI_TOKEN
:alchemyArbitrumMainnetToken config/ALCHEMY_ARBITRUM_MAINNET_TOKEN
:alchemyArbitrumGoerliToken config/ALCHEMY_ARBITRUM_GOERLI_TOKEN
:currentNetwork config/default-network
:previewPrivacy config/blank-preview?}]
{effect request
:dispatch [:navigate-to :generating-keys]
:db (-> db
(dissoc :profile/login)
(dissoc :auth-method)
(assoc :onboarding-2/new-account? true))}))
[{:keys [db] :as cofx}]
(let [{:keys [display-name seed-phrase password image-path color] :as profile}
(:onboarding-2/profile db)]
(rf/merge cofx
{:dispatch [:navigate-to :generating-keys]
:db (-> db
(dissoc :profile/login)
(dissoc :auth-method)
(assoc :onboarding-2/new-account? true))}
(if seed-phrase
(profile.recover/recover-profile-and-login profile)
(profile.create/create-profile-and-login profile)))))
(rf/defn on-delete-profile-success
{:events [:onboarding-2/on-delete-profile-success]}
@ -173,13 +114,11 @@
[{:keys [db]}]
(let [masked-password (get-in db [:onboarding-2/profile :password])
key-uid (get-in db [:profile/profile :key-uid])
biometric-enabled? (=
constants/auth-method-biometric
(get-in db [:onboarding-2/profile :auth-method]))]
biometric-enabled? (= (get-in db [:onboarding-2/profile :auth-method])
constants/auth-method-biometric)]
(cond-> {:dispatch [:navigate-to :identifiers]}
biometric-enabled?
(assoc :biometric/enable-and-save-password
(assoc :keychain/save-password-and-auth-method
{:key-uid key-uid
:masked-password masked-password
:on-success #(log/debug "successfully saved biometric")

View File

@ -18,18 +18,13 @@
(reset! scan-sync-code/dismiss-animations reset-top-animation-fn))
:animations-duration constants/onboarding-modal-animation-duration
:animations-delay constants/onboarding-modal-animation-delay
:top-card {:on-press (fn []
(debounce/dispatch-and-chill [:open-modal
:sign-in-intro]
2000)
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
:top-card {:on-press #(debounce/dispatch-and-chill [:open-modal
:sign-in-intro]
2000)
:heading (i18n/label :t/sign-in)
:animated-heading (i18n/label :t/sign-in-by-syncing)
:accessibility-label :already-use-status-button}
:bottom-card {:on-press (fn []
(rf/dispatch [:navigate-to :new-to-status])
(rf/dispatch
[:hide-terms-of-services-opt-in-screen]))
:bottom-card {:on-press #(rf/dispatch [:navigate-to :new-to-status])
:heading (i18n/label :t/new-to-status)
:accessibility-label :new-to-status-button}}
[quo/text

View File

@ -0,0 +1,42 @@
(ns status-im2.contexts.profile.config
(:require [status-im2.config :as config]
[native-module.core :as native-module]
[clojure.string :as string]))
(defn login
[]
{;; Temporary fix until https://github.com/status-im/status-go/issues/3024 is
;; resolved
:wakuV2Nameserver "1.1.1.1"
:openseaAPIKey config/opensea-api-key
:poktToken config/POKT_TOKEN
:infuraToken config/INFURA_TOKEN
:alchemyOptimismMainnetToken config/ALCHEMY_OPTIMISM_MAINNET_TOKEN
:alchemyOptimismGoerliToken config/ALCHEMY_OPTIMISM_GOERLI_TOKEN
:alchemyArbitrumMainnetToken config/ALCHEMY_ARBITRUM_MAINNET_TOKEN
:alchemyArbitrumGoerliToken config/ALCHEMY_ARBITRUM_GOERLI_TOKEN})
(defn create
[]
(let [log-enabled? (boolean (not-empty config/log-level))]
(merge (login)
{:deviceName (native-module/get-installation-name)
:backupDisabledDataDir (native-module/backup-disabled-data-dir)
:rootKeystoreDir (native-module/keystore-dir)
:logLevel (when log-enabled? config/log-level)
:logEnabled log-enabled?
:logFilePath (native-module/log-file-directory)
:verifyTransactionURL config/verify-transaction-url
:verifyENSURL config/verify-ens-url
:verifyENSContractAddress config/verify-ens-contract-address
:verifyTransactionChainID config/verify-transaction-chain-id
:upstreamConfig config/default-network-rpc-url
:networkId config/default-network-id
:currentNetwork config/default-network
:previewPrivacy config/blank-preview?})))
(defn strip-file-prefix
[path]
(when path
(string/replace-first path "file://" "")))

View File

@ -0,0 +1,23 @@
(ns status-im2.contexts.profile.create.events
(:require [utils.re-frame :as rf]
[native-module.core :as native-module]
[status-im2.contexts.profile.config :as profile.config]
[status-im.ethereum.core :as ethereum]
[utils.security.core :as security]
[re-frame.core :as re-frame]))
(re-frame/reg-fx
::create-profile-and-login
(fn [request]
;;"node.login" signal will be triggered as a callback
(native-module/create-account-and-login request)))
(rf/defn create-profile-and-login
{:events [:profile.create/create-and-login]}
[_ {:keys [display-name password image-path color]}]
{::create-profile-and-login
(merge (profile.config/create)
{:displayName display-name
:password (ethereum/sha3 (security/safe-unmask-data password))
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor color})})

View File

@ -1,6 +1,15 @@
(ns status-im2.contexts.profile.events
(:require [utils.re-frame :as rf]
[clojure.string :as string]))
[clojure.string :as string]
[re-frame.core :as re-frame]
[native-module.core :as native-module]
[status-im2.navigation.events :as navigation]
[status-im2.contexts.profile.login.events :as login]))
(re-frame/reg-fx
:profile/get-profiles-overview
(fn [callback]
(native-module/open-accounts callback)))
(rf/defn profile-selected
{:events [:profile/profile-selected]}
@ -11,6 +20,12 @@
(assoc :key-uid key-uid)
(dissoc :error :password)))})
(rf/defn init-profiles-overview
[{:keys [db] :as cofx} profiles key-uid]
(rf/merge cofx
{:db (assoc db :profile/profiles-overview profiles)}
(profile-selected key-uid)))
(defn rpc->profiles-overview
[{:keys [customizationColor keycard-pairing] :as profile}]
(-> profile
@ -18,18 +33,24 @@
(assoc :customization-color (keyword customizationColor))
(assoc :keycard-pairing (when-not (string/blank? keycard-pairing) keycard-pairing))))
(rf/defn init-profiles-overview
[{:keys [db] :as cofx} profiles]
(let [profiles (reduce
(fn [acc {:keys [key-uid] :as profile}]
(assoc acc key-uid (rpc->profiles-overview profile)))
{}
profiles)
{:keys [key-uid]} (first (sort-by :timestamp > (vals profiles)))]
(rf/merge cofx
{:db (assoc db :profile/profiles-overview profiles)
:keychain/get-auth-method
[key-uid #(rf/dispatch [:multiaccounts.login/get-auth-method-success % key-uid])]
:dispatch-n [[:get-opted-in-to-new-terms-of-service]
[:load-information-box-states]]}
(profile-selected key-uid))))
(defn reduce-profiles
[profiles]
(reduce
(fn [acc {:keys [key-uid] :as profile}]
(assoc acc key-uid (rpc->profiles-overview profile)))
{}
profiles))
(rf/defn get-profiles-overview-success
{:events [:profile/get-profiles-overview-success]}
[cofx profiles-overview]
(if (seq profiles-overview)
(let [profiles (reduce-profiles profiles-overview)
{:keys [key-uid]} (first (sort-by :timestamp > (vals profiles)))]
(rf/merge cofx
(navigation/init-root :profiles)
(init-profiles-overview profiles key-uid)
;;we check if biometric is available, and try to login with it,
;;if succeed "node.login" signal will be triggered
(login/login-with-biometric-if-available key-uid)))
(navigation/init-root cofx :intro)))

View File

@ -1,3 +1,71 @@
(ns status-im2.contexts.profile.login.events)
(ns status-im2.contexts.profile.login.events
(:require [utils.re-frame :as rf]
[status-im.ethereum.core :as ethereum]
[utils.security.core :as security]
[re-frame.core :as re-frame]
[native-module.core :as native-module]
[status-im2.navigation.events :as navigation]
[status-im2.common.keychain.events :as keychain]
[status-im2.common.biometric.events :as biometric]
[status-im2.contexts.profile.config :as profile.config]))
(re-frame/reg-fx
::login
(fn [[key-uid hashed-password]]
;;"node.login" signal will be triggered as a callback
(native-module/login-account
(assoc (profile.config/login) :keyUid key-uid :password hashed-password))))
(rf/defn login
{:events [:profile.login/login]}
[{:keys [db]}]
(let [{:keys [key-uid password]} (:profile/login db)]
{:db (assoc-in db [:profile/login :processing] true)
::login [key-uid (ethereum/sha3 (security/safe-unmask-data password))]}))
(rf/defn login-local-paired-user
{:events [:profile.login/local-paired-user]}
[{:keys [db]}]
(let [{:keys [key-uid password]} (get-in db [:syncing :profile])]
{::login [key-uid password]}))
(rf/defn login-with-biometric-if-available
{:events [:profile.login/login-with-biometric-if-available]}
[_ key-uid]
{:keychain/get-auth-method [key-uid
#(rf/dispatch [:profile.login/get-auth-method-success % key-uid])]})
(rf/defn get-auth-method-success
{:events [:profile.login/get-auth-method-success]}
[{:keys [db]} auth-method]
(merge {:db (assoc db :auth-method auth-method)}
(when (= auth-method keychain/auth-method-biometric)
{:biometric/authenticate
{:on-success #(rf/dispatch [:profile.login/biometric-success])
:on-faile #(rf/dispatch [:profile.login/biometric-auth-fail])}})))
(rf/defn biometric-auth-success
{:events [:profile.login/biometric-success]}
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:profile/login :key-uid])]
(keychain/get-user-password cofx
key-uid
#(rf/dispatch [:profile.login/get-user-password-success %]))))
;; result of :keychain/get-auth-method above
(rf/defn get-user-password-success
{:events [:profile.login/get-user-password-success]}
[{:keys [db] :as cofx} password]
(when password
(rf/merge
cofx
{:db (assoc-in db [:profile/login :password] password)}
(navigation/init-root :progress)
(login))))
(rf/defn biometric-auth-fail
{:events [:profile.login/biometric-auth-fail]}
[{:keys [db] :as cofx} code]
(rf/merge cofx
(navigation/init-root :profiles)
(biometric/show-message code)))

View File

@ -1,4 +1,4 @@
(ns status-im2.contexts.onboarding.profiles.style
(ns status-im2.contexts.profile.profiles.style
(:require [quo2.foundations.colors :as colors]))
;; Profiles Section

View File

@ -1,11 +1,11 @@
(ns status-im2.contexts.onboarding.profiles.view
(ns status-im2.contexts.profile.profiles.view
(:require [native-module.core :as native-module]
[quo2.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.common.confirmation-drawer.view :as confirmation-drawer]
[status-im2.contexts.onboarding.common.background.view :as background]
[status-im2.contexts.onboarding.profiles.style :as style]
[status-im2.contexts.profile.profiles.style :as style]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
@ -15,10 +15,6 @@
[react-native.safe-area :as safe-area]
[clojure.string :as string]))
(defn login-multiaccount
[]
(rf/dispatch [:multiaccounts.login.ui/password-input-submitted]))
(defn new-account-options
[]
[quo/action-drawer
@ -76,8 +72,10 @@
:shell? true}]))
(defn profile-card
[{:keys [name key-uid customization-color keycard-pairing last-index set-hide-profiles]}
index]
[{:keys [name key-uid customization-color keycard-pairing]}
index
_
{:keys [last-index set-hide-profiles]}]
(let [last-item? (= last-index index)
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])]
[quo/profile-card
@ -101,11 +99,7 @@
(defn profiles-section
[{:keys [set-hide-profiles]}]
(let [multiaccounts (vals (rf/sub [:profile/profiles-overview]))
profiles-data (map #(assoc %
:last-index (- (count multiaccounts) 1)
:set-hide-profiles set-hide-profiles)
multiaccounts)]
(let [profiles (vals (rf/sub [:profile/profiles-overview]))]
[rn/view
{:style style/profiles-container}
[rn/view
@ -123,9 +117,11 @@
:accessibility-label :show-new-account-options}
:main-icons/add]]
[rn/flat-list
{:data (sort-by :timestamp > profiles-data)
{:data (sort-by :timestamp > profiles)
:key-fn :key-uid
:content-container-style {:padding-bottom 20}
:render-data {:last-index (- (count profiles) 1)
:set-hide-profiles set-hide-profiles}
:render-fn profile-card}]]))
(defn forget-password-doc
@ -175,19 +171,23 @@
[quo/text {:size :paragraph-2}
(i18n/label :t/forgot-your-password-info-create-new-password-description)]]]]])
(defn- get-error-message
[error]
(if (and (some? error)
(or (= error "file is not a database")
(string/starts-with? error "failed to set ")
(string/starts-with? error "Failed")))
(i18n/label :t/oops-wrong-password)
error))
(defn login-section
[{:keys [set-show-profiles]}]
(let [{:keys [error processing password]} (rf/sub [:profile/login])
{:keys [key-uid name customization-color]} (rf/sub [:profile/login-profile])
sign-in-enabled? (rf/sub [:sign-in-enabled?])
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])
error (if (and (some? error)
(or (= error "file is not a database")
(string/starts-with? error
"failed to set ")
(string/starts-with? error "Failed")))
(i18n/label :t/oops-wrong-password)
error)]
error (get-error-message error)
login-multiaccount #(rf/dispatch [:profile.login/login])]
[rn/keyboard-avoiding-view
{:style style/login-container
:keyboardVerticalOffset (- (safe-area/get-bottom))}
@ -257,7 +257,7 @@
:style {:margin-bottom (+ (safe-area/get-bottom) 12)}}
(i18n/label :t/log-in)]]))
(defn views
(defn view
[]
(let [show-profiles? (reagent/atom false)
set-show-profiles #(reset! show-profiles? true)

View File

@ -0,0 +1,24 @@
(ns status-im2.contexts.profile.recover.events
(:require [utils.security.core :as security]
[status-im.ethereum.core :as ethereum]
[status-im2.contexts.profile.config :as profile.config]
[utils.re-frame :as rf]
[re-frame.core :as re-frame]
[native-module.core :as native-module]))
(re-frame/reg-fx
::restore-profile-and-login
(fn [request]
;;"node.login" signal will be triggered as a callback
(native-module/restore-account-and-login request)))
(rf/defn recover-profile-and-login
{:events [:profile.recover/recover-and-login]}
[_ {:keys [display-name password image-path color seed-phrase]}]
{::restore-profile-and-login
(merge (profile.config/create)
{:displayName display-name
:mnemonic (security/safe-unmask-data seed-phrase)
:password (ethereum/sha3 (security/safe-unmask-data password))
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor color})})

View File

@ -1,62 +1,30 @@
(ns status-im2.events
(:require [re-frame.core :as re-frame]
[status-im2.common.json-rpc.events]
(:require [status-im2.common.json-rpc.events]
[status-im2.common.toasts.events]
[status-im2.contexts.add-new-contact.events]
status-im2.contexts.onboarding.events
[status-im.bottom-sheet.events]
[status-im2.db :as db]
[utils.re-frame :as rf]
[utils.datetime :as datetime]
status-im2.contexts.shell.share.events
status-im2.contexts.syncing.events
status-im2.contexts.chat.events
status-im2.common.password-authentication.events
status-im2.contexts.communities.overview.events
status-im2.contexts.chat.photo-selector.events
[status-im2.contexts.profile.events :as profile.events]
status-im2.common.theme.events
[status-im2.navigation.events :as navigation]
[native-module.core :as native-module]))
(re-frame/reg-cofx :now (fn [coeffects _] (assoc coeffects :now (datetime/timestamp))))
(rf/defn initialize-app-db
"Initialize db to initial state"
[{{:keys [keycard supported-biometric-auth goto-key-storage?]
:network/keys [type]
:keycard/keys [banner-hidden]}
:db}]
{:db (assoc db/app-db
:network/type type
:keycard/banner-hidden banner-hidden
:keycard (dissoc keycard :secrets :pin :application-info)
:supported-biometric-auth supported-biometric-auth
:goto-key-storage? goto-key-storage?)})
(re-frame/reg-fx
:profile/get-profiles-overview
(fn [callback]
(native-module/open-accounts callback)))
(rf/defn get-profiles-overview-success
{:events [:profile/get-profiles-overview-success]}
[cofx profiles-overview]
(if (seq profiles-overview)
(profile.events/init-profiles-overview cofx profiles-overview)
(navigation/init-root cofx :intro)))
status-im2.contexts.profile.events
[status-im.keycard.core :as keycard]))
(rf/defn start-app
{:events [:app-started]}
[cofx]
(rf/merge cofx
{:theme/init-theme nil
:biometric/get-supported-biometric-auth nil
{:db db/app-db
:theme/init-theme nil
:network/listen-to-network-info nil
:keycard/register-card-events nil
:keycard/check-nfc-support nil
:keycard/check-nfc-enabled nil
:keycard/retrieve-pairings nil
:profile/get-profiles-overview #(re-frame/dispatch
:biometric/get-supported-biometric-type nil
;;app starting flow continues in get-profiles-overview
:profile/get-profiles-overview #(rf/dispatch
[:profile/get-profiles-overview-success %])}
(initialize-app-db)))
(keycard/init)))

View File

@ -64,15 +64,6 @@
{:root {:stack {:children [{:component {:name :onboarding-notification
:id :onboarding-notification
:options (options/statusbar-and-navbar-root)}}]
:options (merge (options/default-root)
(options/statusbar-and-navbar-root)
{:topBar (assoc (options/topbar-options) :visible false)})}}}
;; TERMS OF SERVICE
:tos
{:root {:stack {:children [{:component {:name :force-accept-tos
:id :force-accept-tos
:options (get-screen-options :force-accept-tos)}}]
:options (merge (options/default-root)
(options/statusbar-and-navbar-root)
{:topBar (assoc (options/topbar-options) :visible false)})}}}})

View File

@ -19,7 +19,7 @@
[status-im2.contexts.onboarding.sign-in.view :as sign-in]
[status-im2.contexts.onboarding.generating-keys.view :as generating-keys]
[status-im2.contexts.onboarding.enter-seed-phrase.view :as enter-seed-phrase]
[status-im2.contexts.onboarding.profiles.view :as profiles]
[status-im2.contexts.profile.profiles.view :as profiles]
[status-im2.contexts.quo-preview.main :as quo.preview]
[status-im2.contexts.shell.jump-to.view :as shell]
[status-im2.contexts.syncing.scan-sync-code-page.view :as scan-sync-code-page]
@ -143,7 +143,7 @@
{:name :profiles
:options {:theme :dark
:layout options/onboarding-layout}
:component profiles/views}
:component profiles/view}
{:name :new-to-status
:options {:theme :dark

View File

@ -228,12 +228,6 @@
(fn [network]
(tokens/native-currency network)))
(re-frame/reg-sub
:information-box-closed?
:<- [:information-box-states]
(fn [states [_ id]]
(get states id)))
(re-frame/reg-sub
:connectivity/state
:<- [:network-status]

View File

@ -1,80 +0,0 @@
(ns status-im2.subs.onboarding
(:require [quo2.theme :as theme]
[re-frame.core :as re-frame]
[status-im.multiaccounts.recover.core :as recover]
[status-im2.constants :as constants]
[utils.image-server :as image-server]))
(re-frame/reg-sub
:intro-wizard
:<- [:intro-wizard-state]
:<- [:dimensions/window]
(fn [[wizard-state {:keys [width height]}]]
(assoc wizard-state :view-height height :view-width width)))
(re-frame/reg-sub
:intro-wizard/choose-key
:<- [:intro-wizard]
(fn [wizard-state]
(select-keys wizard-state [:multiaccounts :selected-id :view-height])))
(re-frame/reg-sub
:intro-wizard/select-key-storage
:<- [:intro-wizard]
(fn [wizard-state]
(select-keys wizard-state [:selected-storage-type :recovering?])))
(re-frame/reg-sub
:intro-wizard/enter-phrase
:<- [:intro-wizard]
(fn [wizard-state]
(select-keys wizard-state
[:processing?
:passphrase-word-count
:next-button-disabled?
:passphrase-error])))
(re-frame/reg-sub
:intro-wizard/recovery-success
:<- [:intro-wizard]
(fn [wizard-state]
{:pubkey (get-in wizard-state [:derived constants/path-whisper-keyword :public-key])
:compressed-key (get-in wizard-state [:derived constants/path-whisper-keyword :compressed-key])
:name (get-in wizard-state [:derived constants/path-whisper-keyword :name])
:processing? (:processing? wizard-state)}))
(re-frame/reg-sub
:intro-wizard/recover-existing-account?
:<- [:intro-wizard]
:<- [:profile/profiles-overview]
(fn [[intro-wizard multiaccounts]]
(recover/existing-account? (:root-key intro-wizard) multiaccounts)))
(re-frame/reg-sub
:profile/login-profiles-picture
:<- [:profile/profiles-overview]
:<- [:mediaserver/port]
(fn [[multiaccounts port] [_ target-key-uid]]
(let [image-name (-> multiaccounts
(get-in [target-key-uid :images])
first
:type)]
(when image-name
(image-server/get-account-image-uri {:port port
:image-name image-name
:key-uid target-key-uid
:theme (theme/get-theme)
:ring? true})))))
(defn login-profile-keycard-pairing
"Compute the keycard-pairing value of the multiaccount selected for login"
[db _]
(when-let [acc-to-login (-> db :profile/login)]
(-> db
:profile/profiles-overview
(get (:key-uid acc-to-login))
:keycard-pairing)))
(re-frame/reg-sub
:intro-wizard/acc-to-login-keycard-pairing
login-profile-keycard-pairing)

View File

@ -1,38 +0,0 @@
(ns status-im2.subs.onboarding-test
(:require [cljs.test :as t]
[quo2.theme :as theme]
[re-frame.db :as rf-db]
status-im2.subs.onboarding
[test-helpers.unit :as h]
[utils.image-server :as image-server]
[utils.re-frame :as rf]))
(def key-uid "0x1")
(def port "mediaserver-port")
(def cur-theme :current-theme)
(h/deftest-sub :profile/login-profiles-picture
[sub-name]
(with-redefs [image-server/get-account-image-uri identity
theme/get-theme (constantly cur-theme)]
(t/testing "nil when no images"
(swap! rf-db/app-db assoc :profile/profiles-overview {key-uid {}})
(t/is (nil? (rf/sub [sub-name key-uid]))))
(t/testing "nil when no key-uid"
(swap! rf-db/app-db assoc :profile/profiles-overview {key-uid {}})
(t/is (nil? (rf/sub [sub-name "0x2"]))))
(t/testing "result from image-server/get-account-image-uri"
(swap!
rf-db/app-db
assoc
:profile/profiles-overview {key-uid {:images [{:type "large"}
{:type "thumbnail"}]}}
:mediaserver/port port)
(t/is (= (rf/sub [sub-name key-uid])
{:port port
:image-name "large"
:key-uid key-uid
:theme cur-theme
:ring? true})))))

View File

@ -23,6 +23,22 @@
(fn [{:keys [customization-color]}]
(or customization-color constants/profile-default-color)))
(re-frame/reg-sub
:profile/login-profiles-picture
:<- [:profile/profiles-overview]
:<- [:mediaserver/port]
(fn [[multiaccounts port] [_ target-key-uid]]
(let [image-name (-> multiaccounts
(get-in [target-key-uid :images])
first
:type)]
(when image-name
(image-server/get-account-image-uri {:port port
:image-name image-name
:key-uid target-key-uid
:theme (theme/get-theme)
:ring? true})))))
(re-frame/reg-sub
:multiaccount/public-key
:<- [:profile/profile]

View File

@ -15,7 +15,6 @@
status-im2.subs.mailservers
status-im2.subs.profile
status-im2.subs.networks
status-im2.subs.onboarding
status-im2.subs.pairing
status-im2.subs.search
status-im2.subs.shell
@ -66,7 +65,7 @@
(reg-root-key-sub :networks/manage :networks/manage)
(reg-root-key-sub :get-pairing-installations :pairing/installations)
(reg-root-key-sub :tooltips :tooltips)
(reg-root-key-sub :supported-biometric-auth :supported-biometric-auth)
(reg-root-key-sub :biometric/supported-type :biometric/supported-type)
(reg-root-key-sub :connectivity/ui-status-properties :connectivity/ui-status-properties)
(reg-root-key-sub :logged-in-since :logged-in-since)
(reg-root-key-sub :app-state :app-state)
@ -231,8 +230,6 @@
(reg-root-key-sub :auth-method :auth-method)
(reg-root-key-sub :tos-accept-next-root :tos-accept-next-root)
;; keycard
(reg-root-key-sub :keycard/banner-hidden :keycard/banner-hidden)
@ -290,7 +287,5 @@
(reg-root-key-sub :messenger/started? :messenger/started?)
(reg-root-key-sub :information-box-states :information-box-states)
; Messages home view -> tabs
(reg-root-key-sub :messages-home/selected-tab :messages-home/selected-tab)

View File

@ -1,6 +1,5 @@
(ns status-im2.subs.subs-test
(:require [cljs.test :refer [deftest is testing]]
[status-im2.subs.onboarding :as onboarding]
[status-im2.subs.wallet.transactions :as wallet.transactions]))
(def transactions
@ -23,20 +22,3 @@
(testing "Check if transactions are sorted by date"
(is (= (wallet.transactions/group-transactions-by-date transactions)
grouped-transactions))))
(deftest login-profile-keycard-pairing
(testing "returns nil when no :profile/login"
(let [res (onboarding/login-profile-keycard-pairing
{:profile/login nil
:profile/profiles-overview
{"0x1" {:keycard-pairing "keycard-pairing-code"}}}
{})]
(is (nil? res))))
(testing "returns :keycard-pairing when :profile/login is present"
(let [res (onboarding/login-profile-keycard-pairing
{:profile/login {:key-uid "0x1"}
:profile/profiles-overview
{"0x1" {:keycard-pairing "keycard-pairing-code"}}}
{})]
(is (= res "keycard-pairing-code")))))

View File

@ -2,11 +2,14 @@
(:require-macros utils.re-frame)
(:require [re-frame.core :as re-frame]
[re-frame.interceptor :as interceptor]
[taoensso.timbre :as log])
[taoensso.timbre :as log]
[utils.datetime :as datetime])
(:refer-clojure :exclude [merge reduce]))
(def handler-nesting-level (atom 0))
(re-frame/reg-cofx :now (fn [coeffects _] (assoc coeffects :now (datetime/timestamp))))
(def debug-handlers-names
"Interceptor which logs debug information to js/console for each event."
(interceptor/->interceptor