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:
parent
229a806f12
commit
4decba8d00
|
@ -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);
|
||||
|
|
|
@ -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 isn’t migrated when restoring another device’s 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."))))))
|
|
@ -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"))))))
|
|
@ -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 %])}}))
|
||||
|
|
|
@ -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])))))))))
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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])))))
|
||||
|
|
|
@ -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)))
|
|
@ -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)})
|
||||
|
|
|
@ -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))))))))
|
|
@ -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)})
|
||||
|
|
|
@ -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))))))))
|
|
@ -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}})
|
|
@ -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]))))))
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
%])]}))
|
||||
|
|
|
@ -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)))))
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)}]))
|
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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]))
|
|
@ -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 isn’t migrated when restoring another device’s 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]})
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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})
|
|
@ -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 %))))))
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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://" "")))
|
|
@ -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})})
|
|
@ -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)))
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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})})
|
|
@ -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)))
|
||||
|
|
|
@ -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)})}}}})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
|
@ -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})))))
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")))))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue