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) {
|
void _GenerateAlias(const FunctionCallbackInfo<Value>& args) {
|
||||||
Isolate* isolate = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
Local<Context> context = isolate->GetCurrentContext();
|
Local<Context> context = isolate->GetCurrentContext();
|
||||||
|
@ -1929,6 +1960,7 @@ void init(Local<Object> exports) {
|
||||||
NODE_SET_METHOD(exports, "hashMessage", _HashMessage);
|
NODE_SET_METHOD(exports, "hashMessage", _HashMessage);
|
||||||
NODE_SET_METHOD(exports, "resetChainData", _ResetChainData);
|
NODE_SET_METHOD(exports, "resetChainData", _ResetChainData);
|
||||||
NODE_SET_METHOD(exports, "saveAccountAndLogin", _SaveAccountAndLogin);
|
NODE_SET_METHOD(exports, "saveAccountAndLogin", _SaveAccountAndLogin);
|
||||||
|
NODE_SET_METHOD(exports, "createAccountAndLogin", _CreateAccountAndLogin);
|
||||||
NODE_SET_METHOD(exports, "generateAlias", _GenerateAlias);
|
NODE_SET_METHOD(exports, "generateAlias", _GenerateAlias);
|
||||||
NODE_SET_METHOD(exports, "validateMnemonic", _ValidateMnemonic);
|
NODE_SET_METHOD(exports, "validateMnemonic", _ValidateMnemonic);
|
||||||
NODE_SET_METHOD(exports, "multiformatSerializePublicKey", _MultiformatSerializePublicKey);
|
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.log-level.core
|
||||||
status-im.mailserver.constants
|
status-im.mailserver.constants
|
||||||
[status-im.mailserver.core :as mailserver]
|
[status-im.mailserver.core :as mailserver]
|
||||||
[status-im.multiaccounts.biometric.core :as biometric]
|
|
||||||
status-im.multiaccounts.login.core
|
status-im.multiaccounts.login.core
|
||||||
status-im.multiaccounts.logout.core
|
status-im.multiaccounts.logout.core
|
||||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||||
|
@ -63,7 +62,8 @@
|
||||||
[react-native.platform :as platform]
|
[react-native.platform :as platform]
|
||||||
status-im2.contexts.chat.home.events
|
status-im2.contexts.chat.home.events
|
||||||
status-im2.contexts.communities.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
|
(re-frame/reg-fx
|
||||||
:dismiss-keyboard
|
:dismiss-keyboard
|
||||||
|
@ -124,33 +124,26 @@
|
||||||
[(get-in db [:profile/profile :appearance])
|
[(get-in db [:profile/profile :appearance])
|
||||||
(:view-id db) true]})))
|
(:view-id db) true]})))
|
||||||
|
|
||||||
(def authentication-options
|
(defn- on-biometric-auth-fail
|
||||||
{:reason (i18n/label :t/biometric-auth-reason-login)})
|
[{:keys [code]}]
|
||||||
|
(if (= code "USER_FALLBACK")
|
||||||
(defn- on-biometric-auth-result
|
(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
|
||||||
[{:keys [bioauth-success bioauth-code bioauth-message]}]
|
(utils/show-confirmation
|
||||||
(when-not bioauth-success
|
{:title (i18n/label :t/biometric-auth-confirm-title)
|
||||||
(if (= bioauth-code "USER_FALLBACK")
|
:content (i18n/label :t/biometric-auth-confirm-message)
|
||||||
(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
|
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
|
||||||
(utils/show-confirmation
|
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
|
||||||
{:title (i18n/label :t/biometric-auth-confirm-title)
|
:on-accept #(biometric/authenticate nil {:on-fail on-biometric-auth-fail})
|
||||||
:content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message))
|
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))
|
||||||
: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])}))))
|
|
||||||
|
|
||||||
(rf/defn on-return-from-background
|
(rf/defn on-return-from-background
|
||||||
[{:keys [db now] :as cofx}]
|
[{:keys [db now] :as cofx}]
|
||||||
(let [new-account? (get db :onboarding-2/new-account?)
|
(let [new-account? (get db :onboarding-2/new-account?)
|
||||||
app-in-background-since (get db :app-in-background-since)
|
app-in-background-since (get db :app-in-background-since)
|
||||||
signed-up? (get-in db [:profile/profile :signed-up?])
|
signed-up? (get-in db [:profile/profile :signed-up?])
|
||||||
biometric-auth? (= (:auth-method db) "biometric")
|
|
||||||
requires-bio-auth (and
|
requires-bio-auth (and
|
||||||
signed-up?
|
signed-up?
|
||||||
biometric-auth?
|
(= (:auth-method db) "biometric")
|
||||||
(some? app-in-background-since)
|
(some? app-in-background-since)
|
||||||
(>= (- now app-in-background-since)
|
(>= (- now app-in-background-since)
|
||||||
constants/ms-in-bg-for-require-bioauth))]
|
constants/ms-in-bg-for-require-bioauth))]
|
||||||
|
@ -163,7 +156,7 @@
|
||||||
#(when-let [chat-id (:current-chat-id db)]
|
#(when-let [chat-id (:current-chat-id db)]
|
||||||
{:dispatch [:chat/mark-all-as-read chat-id]})
|
{:dispatch [:chat/mark-all-as-read chat-id]})
|
||||||
#(when requires-bio-auth
|
#(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
|
(rf/defn on-going-in-background
|
||||||
[{:keys [db now]}]
|
[{:keys [db now]}]
|
||||||
|
@ -274,53 +267,3 @@
|
||||||
cofx
|
cofx
|
||||||
(navigation/open-modal :buy-crypto nil)
|
(navigation/open-modal :buy-crypto nil)
|
||||||
(wallet/keep-watching-history)))
|
(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]))
|
(rf/dispatch [:app-started]))
|
||||||
|
|
||||||
(defn generate-and-derive-addresses!
|
|
||||||
[]
|
|
||||||
(rf/dispatch [:generate-and-derive-addresses]))
|
|
||||||
|
|
||||||
(defn create-multiaccount!
|
(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
|
(defn assert-app-initialized
|
||||||
[]
|
[]
|
||||||
|
@ -82,15 +79,12 @@
|
||||||
(initialize-app!) ; initialize app
|
(initialize-app!) ; initialize app
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!) ; generate 5 new keys
|
(create-multiaccount!) ; create a multiaccount
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for ; wait for login
|
||||||
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!) ; create a multiaccount
|
(assert-messenger-started)
|
||||||
(rf-test/wait-for ; wait for login
|
(logout!)
|
||||||
[::transport/messenger-started]
|
(rf-test/wait-for [::logout/logout-method])))))
|
||||||
(assert-messenger-started)
|
|
||||||
(logout!)
|
|
||||||
(rf-test/wait-for [::logout/logout-method]))))))
|
|
||||||
|
|
||||||
(deftest create-community-test
|
(deftest create-community-test
|
||||||
(log/info "====== create-community-test ==================")
|
(log/info "====== create-community-test ==================")
|
||||||
|
@ -98,22 +92,19 @@
|
||||||
(initialize-app!) ; initialize app
|
(initialize-app!) ; initialize app
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!) ; generate 5 new keys
|
(create-multiaccount!) ; create a multiaccount
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for ; wait for login
|
||||||
[:multiaccount-generate-and-derive-addresses-success]
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!) ; create a multiaccount
|
(assert-messenger-started)
|
||||||
(rf-test/wait-for ; wait for login
|
(rf/dispatch-sync [:legacy-only-for-e2e/open-create-community])
|
||||||
[::transport/messenger-started]
|
(doseq [[k v] (dissoc community :membership)]
|
||||||
(assert-messenger-started)
|
(rf/dispatch-sync [:status-im.communities.core/create-field k v]))
|
||||||
(rf/dispatch-sync [:legacy-only-for-e2e/open-create-community])
|
(rf/dispatch [:status-im.communities.core/create-confirmation-pressed])
|
||||||
(doseq [[k v] (dissoc community :membership)]
|
(rf-test/wait-for
|
||||||
(rf/dispatch-sync [:status-im.communities.core/create-field k v]))
|
[:status-im.communities.core/community-created]
|
||||||
(rf/dispatch [:status-im.communities.core/create-confirmation-pressed])
|
(assert-community-created)
|
||||||
(rf-test/wait-for
|
(logout!)
|
||||||
[:status-im.communities.core/community-created]
|
(rf-test/wait-for [::logout/logout-method]))))))
|
||||||
(assert-community-created)
|
|
||||||
(logout!)
|
|
||||||
(rf-test/wait-for [::logout/logout-method])))))))
|
|
||||||
|
|
||||||
(deftest create-wallet-account-test
|
(deftest create-wallet-account-test
|
||||||
(log/info "====== create-wallet-account-test ==================")
|
(log/info "====== create-wallet-account-test ==================")
|
||||||
|
@ -121,19 +112,16 @@
|
||||||
(initialize-app!)
|
(initialize-app!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!) ; generate 5 new keys
|
(create-multiaccount!) ; create a multiaccount
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for ; wait for login
|
||||||
[:multiaccount-generate-and-derive-addresses-success]
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!) ; create a multiaccount
|
(assert-messenger-started)
|
||||||
(rf-test/wait-for ; wait for login
|
(create-new-account!) ; create a new account
|
||||||
[::transport/messenger-started]
|
(rf-test/wait-for
|
||||||
(assert-messenger-started)
|
[:wallet.accounts/account-stored]
|
||||||
(create-new-account!) ; create a new account
|
(assert-new-account-created) ; assert account was created
|
||||||
(rf-test/wait-for
|
(logout!)
|
||||||
[:wallet.accounts/account-stored]
|
(rf-test/wait-for [::logout/logout-method]))))))
|
||||||
(assert-new-account-created) ; assert account was created
|
|
||||||
(logout!)
|
|
||||||
(rf-test/wait-for [::logout/logout-method])))))))
|
|
||||||
|
|
||||||
(deftest back-up-seed-phrase-test
|
(deftest back-up-seed-phrase-test
|
||||||
(log/info "========= back-up-seed-phrase-test ==================")
|
(log/info "========= back-up-seed-phrase-test ==================")
|
||||||
|
@ -141,30 +129,27 @@
|
||||||
(initialize-app!)
|
(initialize-app!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!)
|
(create-multiaccount!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:multiaccount-generate-and-derive-addresses-success]
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!)
|
(assert-messenger-started)
|
||||||
(rf-test/wait-for
|
(rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user
|
||||||
[::transport/messenger-started]
|
(rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words
|
||||||
(assert-messenger-started)
|
(let [{:keys [mnemonic]} @(rf/subscribe [:profile/profile])
|
||||||
(rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user
|
seed @(rf/subscribe [:my-profile/seed])
|
||||||
(rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words
|
word1 (second (:first-word seed))
|
||||||
(let [{:keys [mnemonic]} @(rf/subscribe [:profile/profile])
|
word2 (second (:second-word seed))]
|
||||||
seed @(rf/subscribe [:my-profile/seed])
|
(is (= 12 (count (string/split mnemonic #" ")))) ; assert 12-word seed phrase
|
||||||
word1 (second (:first-word seed))
|
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word1])
|
||||||
word2 (second (:second-word seed))]
|
(rf/dispatch-sync [:my-profile/set-step :second-word])
|
||||||
(is (= 12 (count (string/split mnemonic #" ")))) ; assert 12-word seed phrase
|
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word2])
|
||||||
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word1])
|
;; TODO: refactor (defn next-handler) & (defn enter-word) to improve test coverage?
|
||||||
(rf/dispatch-sync [:my-profile/set-step :second-word])
|
(rf/dispatch [:my-profile/finish])
|
||||||
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word2])
|
(rf-test/wait-for
|
||||||
;; TODO: refactor (defn next-handler) & (defn enter-word) to improve test coverage?
|
[:my-profile/finish-success]
|
||||||
(rf/dispatch [:my-profile/finish])
|
(is (nil? @(rf/subscribe [:mnemonic]))) ; assert seed phrase has been removed
|
||||||
(rf-test/wait-for
|
(logout!)
|
||||||
[:my-profile/finish-success]
|
(rf-test/wait-for [::logout/logout-method])))))))
|
||||||
(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-name "Narrow Frail Lemming")
|
||||||
(def multiaccount-mnemonic
|
(def multiaccount-mnemonic
|
||||||
|
@ -180,20 +165,17 @@
|
||||||
(initialize-app!)
|
(initialize-app!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!)
|
(create-multiaccount!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!)
|
(assert-messenger-started)
|
||||||
|
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[::transport/messenger-started]
|
[:chat/one-to-one-chat-created]
|
||||||
(assert-messenger-started)
|
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
||||||
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
||||||
(rf-test/wait-for
|
(logout!)
|
||||||
[:chat/one-to-one-chat-created]
|
(rf-test/wait-for [::logout/logout-method]))))))
|
||||||
(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
|
(deftest delete-chat-test
|
||||||
(log/info "========= delete-chat-test ==================")
|
(log/info "========= delete-chat-test ==================")
|
||||||
|
@ -201,23 +183,20 @@
|
||||||
(initialize-app!)
|
(initialize-app!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!)
|
(create-multiaccount!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!)
|
(assert-messenger-started)
|
||||||
|
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[::transport/messenger-started]
|
[:chat/one-to-one-chat-created]
|
||||||
(assert-messenger-started)
|
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
||||||
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
||||||
(rf-test/wait-for
|
(is @(rf/subscribe [:chats/chat chat-id]))
|
||||||
[:chat/one-to-one-chat-created]
|
(rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id])
|
||||||
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
|
||||||
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
(logout!)
|
||||||
(is @(rf/subscribe [:chats/chat chat-id]))
|
(rf-test/wait-for [::logout/logout-method]))))))
|
||||||
(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
|
(deftest mute-chat-test
|
||||||
(log/info "========= mute-chat-test ==================")
|
(log/info "========= mute-chat-test ==================")
|
||||||
|
@ -225,29 +204,26 @@
|
||||||
(initialize-app!)
|
(initialize-app!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!)
|
(create-multiaccount!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!)
|
(assert-messenger-started)
|
||||||
|
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[::transport/messenger-started]
|
[:chat/one-to-one-chat-created]
|
||||||
(assert-messenger-started)
|
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
||||||
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
(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
|
(rf-test/wait-for
|
||||||
[:chat/one-to-one-chat-created]
|
[:chat/mute-successfully]
|
||||||
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
(is @(rf/subscribe [:chats/muted chat-id]))
|
||||||
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
(rf/dispatch-sync [:chat.ui/mute chat-id false])
|
||||||
(is @(rf/subscribe [:chats/chat chat-id]))
|
|
||||||
(rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted])
|
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:chat/mute-successfully]
|
[:chat/mute-successfully]
|
||||||
(is @(rf/subscribe [:chats/muted chat-id]))
|
(is (not @(rf/subscribe [:chats/muted chat-id])))
|
||||||
(rf/dispatch-sync [:chat.ui/mute chat-id false])
|
(logout!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for [::logout/logout-method]))))))))
|
||||||
[:chat/mute-successfully]
|
|
||||||
(is (not @(rf/subscribe [:chats/muted chat-id])))
|
|
||||||
(logout!)
|
|
||||||
(rf-test/wait-for [::logout/logout-method])))))))))
|
|
||||||
|
|
||||||
(deftest add-contact-test
|
(deftest add-contact-test
|
||||||
(log/info "========= add-contact-test ==================")
|
(log/info "========= add-contact-test ==================")
|
||||||
|
@ -260,27 +236,24 @@
|
||||||
(initialize-app!)
|
(initialize-app!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:profile/get-profiles-overview-success]
|
[:profile/get-profiles-overview-success]
|
||||||
(generate-and-derive-addresses!)
|
(create-multiaccount!)
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:multiaccount-generate-and-derive-addresses-success]
|
[::transport/messenger-started]
|
||||||
(create-multiaccount!)
|
(assert-messenger-started)
|
||||||
|
;; search for contact using compressed key
|
||||||
|
(rf/dispatch [:contacts/set-new-identity compressed-key])
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[::transport/messenger-started]
|
[:contacts/set-new-identity-success]
|
||||||
(assert-messenger-started)
|
(let [new-identity @(rf/subscribe [:contacts/new-identity])]
|
||||||
;; search for contact using compressed key
|
(is (= public-key (:public-key new-identity)))
|
||||||
(rf/dispatch [:contacts/set-new-identity compressed-key])
|
(is (= :valid (:state new-identity))))
|
||||||
|
;; click 'view profile' button
|
||||||
|
(rf/dispatch [:chat.ui/show-profile public-key])
|
||||||
(rf-test/wait-for
|
(rf-test/wait-for
|
||||||
[:contacts/set-new-identity-success]
|
[:contacts/build-contact]
|
||||||
(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
|
(rf-test/wait-for
|
||||||
[:contacts/build-contact]
|
[:contacts/contact-built]
|
||||||
(rf-test/wait-for
|
(let [contact @(rf/subscribe [:contacts/current-contact])]
|
||||||
[:contacts/contact-built]
|
(is (= three-words-name (:primary-name contact))))
|
||||||
(let [contact @(rf/subscribe [:contacts/current-contact])]
|
(logout!)
|
||||||
(is (= three-words-name (:primary-name contact))))
|
(rf-test/wait-for [::logout/logout-method])))))))))
|
||||||
(logout!)
|
|
||||||
(rf-test/wait-for [::logout/logout-method]))))))))))
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
[status-im.keycard.sign :as sign]
|
[status-im.keycard.sign :as sign]
|
||||||
status-im.keycard.unpair
|
status-im.keycard.unpair
|
||||||
[status-im.keycard.wallet :as wallet]
|
[status-im.keycard.wallet :as wallet]
|
||||||
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
|
|
||||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.datetime :as datetime]
|
[utils.datetime :as datetime]
|
||||||
|
@ -589,7 +588,8 @@
|
||||||
(when (and (= card-state :profile/profile)
|
(when (and (= card-state :profile/profile)
|
||||||
(= flow :import))
|
(= flow :import))
|
||||||
(if (common/find-multiaccount-by-key-uid db key-uid)
|
(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?
|
(if paired?
|
||||||
(load-recovery-pin-screen)
|
(load-recovery-pin-screen)
|
||||||
(recovery/load-pair-screen))))
|
(recovery/load-pair-screen))))
|
||||||
|
@ -681,3 +681,11 @@
|
||||||
{:events [:keycard.callback/stop-nfc-failure]}
|
{:events [:keycard.callback/stop-nfc-failure]}
|
||||||
[{:keys [db]} _]
|
[{:keys [db]} _]
|
||||||
(log/debug "[keycard] nfc failed stopping")) ;; leave current value on :nfc-running
|
(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
|
{:db (-> db
|
||||||
(update :keycard dissoc :flow)
|
(update :keycard dissoc :flow)
|
||||||
(dissoc :restored-account?))}
|
(dissoc :restored-account?))}
|
||||||
(multiaccounts.create/prepare-intro-wizard)
|
|
||||||
(if (pos? (count accs))
|
(if (pos? (count accs))
|
||||||
(navigation/navigate-to :get-your-keys nil)
|
(navigation/navigate-to :get-your-keys nil)
|
||||||
(navigation/set-stack-root :onboarding [:get-your-keys])))))
|
(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 _]
|
(fn [cofx _]
|
||||||
(assoc cofx :signing-phrase (signing-phrase/generate))))
|
(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
|
(re-frame/reg-fx
|
||||||
:multiaccount-generate-and-derive-addresses
|
:multiaccount-generate-and-derive-addresses
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -116,10 +79,6 @@
|
||||||
(dissoc :recovered-account?))
|
(dissoc :recovered-account?))
|
||||||
:multiaccount-generate-and-derive-addresses nil})
|
: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
|
(rf/defn save-multiaccount-and-login-with-keycard
|
||||||
[_ args]
|
[_ args]
|
||||||
{:keycard/save-multiaccount-and-login args})
|
{:keycard/save-multiaccount-and-login args})
|
||||||
|
@ -255,27 +214,3 @@
|
||||||
settings
|
settings
|
||||||
(node/get-new-config db)
|
(node/get-new-config db)
|
||||||
accounts-data)))))
|
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]
|
[status-im.fleet.core :as fleet]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[status-im.mobile-sync-settings.core :as mobile-network]
|
[status-im.mobile-sync-settings.core :as mobile-network]
|
||||||
[status-im.multiaccounts.biometric.core :as biometric]
|
|
||||||
[status-im.multiaccounts.core :as multiaccounts]
|
[status-im.multiaccounts.core :as multiaccounts]
|
||||||
[native-module.core :as native-module]
|
[native-module.core :as native-module]
|
||||||
[status-im.notifications.core :as notifications]
|
[status-im.notifications.core :as notifications]
|
||||||
[status-im.popover.core :as popover]
|
|
||||||
[status-im.signing.eip1559 :as eip1559]
|
[status-im.signing.eip1559 :as eip1559]
|
||||||
[status-im.transport.core :as transport]
|
[status-im.transport.core :as transport]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im2.config :as config]
|
[status-im2.config :as config]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im.utils.keychain.core :as keychain]
|
|
||||||
[status-im.utils.mobile-sync :as utils.mobile-sync]
|
[status-im.utils.mobile-sync :as utils.mobile-sync]
|
||||||
[status-im.utils.platform :as platform]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
|
@ -46,7 +43,7 @@
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
|
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
|
||||||
[utils.security.core :as security]
|
[utils.security.core :as security]
|
||||||
[status-im.keycard.common :as keycard.common]))
|
[status-im2.common.keychain.events :as keychain]))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
::initialize-transactions-management-enabled
|
::initialize-transactions-management-enabled
|
||||||
|
@ -56,22 +53,6 @@
|
||||||
:transactions-management-enabled?
|
:transactions-management-enabled?
|
||||||
callback))))
|
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
|
(re-frame/reg-fx
|
||||||
::export-db
|
::export-db
|
||||||
(fn [[key-uid account-data hashed-password callback]]
|
(fn [[key-uid account-data hashed-password callback]]
|
||||||
|
@ -177,27 +158,8 @@
|
||||||
(wallet/request-current-block-update))
|
(wallet/request-current-block-update))
|
||||||
(prices/update-prices)))
|
(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
|
(rf/defn export-db-submitted
|
||||||
{:events [:multiaccounts.login.ui/export-db-submitted]}
|
{:events [:multiaccounts.login.ui/export-db-submitted]}
|
||||||
|
@ -392,7 +354,7 @@
|
||||||
|
|
||||||
(rf/defn get-node-config-callback
|
(rf/defn get-node-config-callback
|
||||||
{:events [::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)]
|
(let [node-config (types/json->clj node-config-json)]
|
||||||
{:db (assoc-in db
|
{:db (assoc-in db
|
||||||
[:profile/profile :wakuv2-config]
|
[:profile/profile :wakuv2-config]
|
||||||
|
@ -414,14 +376,11 @@
|
||||||
(get db :onboarding-2/new-account?)
|
(get db :onboarding-2/new-account?)
|
||||||
{:dispatch [:onboarding-2/finalize-setup]}
|
{:dispatch [:onboarding-2/finalize-setup]}
|
||||||
|
|
||||||
(get db :tos/accepted?)
|
:else
|
||||||
(rf/merge
|
(rf/merge
|
||||||
cofx
|
cofx
|
||||||
(multiaccounts/switch-theme nil :shell-stack)
|
(multiaccounts/switch-theme nil :shell-stack)
|
||||||
(navigation/init-root :shell-stack))
|
(navigation/init-root :shell-stack)))))
|
||||||
|
|
||||||
:else
|
|
||||||
{:dispatch [:init-root :tos]})))
|
|
||||||
|
|
||||||
(rf/defn get-settings-callback
|
(rf/defn get-settings-callback
|
||||||
{:events [::get-settings-callback]}
|
{:events [::get-settings-callback]}
|
||||||
|
@ -448,7 +407,6 @@
|
||||||
#(do (re-frame/dispatch [:chats-list/load-success %])
|
#(do (re-frame/dispatch [:chats-list/load-success %])
|
||||||
(rf/dispatch [:communities/get-user-requests-to-join])
|
(rf/dispatch [:communities/get-user-requests-to-join])
|
||||||
(re-frame/dispatch [::get-chats-callback]))})
|
(re-frame/dispatch [::get-chats-callback]))})
|
||||||
(initialize-wallet-connect)
|
|
||||||
(get-node-config)
|
(get-node-config)
|
||||||
(communities/fetch)
|
(communities/fetch)
|
||||||
(contract-communities/fetch-contract-communities)
|
(contract-communities/fetch-contract-communities)
|
||||||
|
@ -530,11 +488,9 @@
|
||||||
(defn get-new-auth-method
|
(defn get-new-auth-method
|
||||||
[auth-method save-password?]
|
[auth-method save-password?]
|
||||||
(when save-password?
|
(when save-password?
|
||||||
(when-not (or (= keychain/auth-method-biometric auth-method)
|
(when-not (= keychain/auth-method-biometric auth-method)
|
||||||
(= keychain/auth-method-password auth-method))
|
(when (= auth-method keychain/auth-method-biometric-prepare)
|
||||||
(if (= auth-method keychain/auth-method-biometric-prepare)
|
keychain/auth-method-biometric))))
|
||||||
keychain/auth-method-biometric
|
|
||||||
keychain/auth-method-password))))
|
|
||||||
|
|
||||||
(rf/defn login-only-events
|
(rf/defn login-only-events
|
||||||
[{:keys [db] :as cofx} key-uid password save-password?]
|
[{:keys [db] :as cofx} key-uid password save-password?]
|
||||||
|
@ -549,8 +505,6 @@
|
||||||
[{:method "settings_getSettings"
|
[{:method "settings_getSettings"
|
||||||
:on-success #(re-frame/dispatch [::get-settings-callback %])}]}
|
:on-success #(re-frame/dispatch [::get-settings-callback %])}]}
|
||||||
(notifications/load-notification-preferences)
|
(notifications/load-notification-preferences)
|
||||||
(when save-password?
|
|
||||||
(keychain/save-user-password key-uid password))
|
|
||||||
(keychain/save-auth-method key-uid
|
(keychain/save-auth-method key-uid
|
||||||
(or new-auth-method auth-method keychain/auth-method-none)))))
|
(or new-auth-method auth-method keychain/auth-method-none)))))
|
||||||
|
|
||||||
|
@ -560,14 +514,13 @@
|
||||||
db
|
db
|
||||||
{:keys [creating?]} (:profile/login db)
|
{:keys [creating?]} (:profile/login db)
|
||||||
first-account? (and creating? (empty? profiles-overview))
|
first-account? (and creating? (empty? profiles-overview))
|
||||||
tos-accepted? (get db :tos/accepted?)
|
|
||||||
{:networks/keys [current-network networks]} db
|
{:networks/keys [current-network networks]} db
|
||||||
network-id (str (get-in networks [current-network :config :NetworkId]))]
|
network-id (str (get-in networks [current-network :config :NetworkId]))]
|
||||||
(shell.utils/change-selected-stack-id :communities-stack true nil)
|
(shell.utils/change-selected-stack-id :communities-stack true nil)
|
||||||
(rf/merge cofx
|
(rf/merge cofx
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(dissoc :profile/login)
|
(dissoc :profile/login)
|
||||||
(assoc :tos/next-root :enable-notifications :chats/loading? false)
|
(assoc :chats/loading? false)
|
||||||
(assoc-in [:profile/profile :multiaccounts/first-account]
|
(assoc-in [:profile/profile :multiaccounts/first-account]
|
||||||
first-account?))
|
first-account?))
|
||||||
::get-tokens [network-id wallet-accounts recovered-account?]}
|
::get-tokens [network-id wallet-accounts recovered-account?]}
|
||||||
|
@ -586,7 +539,7 @@
|
||||||
(boolean (get-in cofx [:db :keycard :flow])))
|
(boolean (get-in cofx [:db :keycard :flow])))
|
||||||
|
|
||||||
(defn on-login-update-db
|
(defn on-login-update-db
|
||||||
[db login-only? now]
|
[db now]
|
||||||
(-> db
|
(-> db
|
||||||
(dissoc :connectivity/ui-status-properties)
|
(dissoc :connectivity/ui-status-properties)
|
||||||
(update :keycard dissoc :from-key-storage-and-migration?)
|
(update :keycard dissoc :from-key-storage-and-migration?)
|
||||||
|
@ -595,10 +548,6 @@
|
||||||
:card-read-in-progress?
|
:card-read-in-progress?
|
||||||
:pin
|
:pin
|
||||||
:profile/profile)
|
:profile/profile)
|
||||||
(assoc :tos-accept-next-root
|
|
||||||
(if login-only?
|
|
||||||
:shell-stack
|
|
||||||
:onboarding-notification))
|
|
||||||
(assoc :logged-in-since now)
|
(assoc :logged-in-since now)
|
||||||
(assoc :view-id :home)))
|
(assoc :view-id :home)))
|
||||||
|
|
||||||
|
@ -620,7 +569,7 @@
|
||||||
"login-only?" login-only?
|
"login-only?" login-only?
|
||||||
"recovered-account?" recovered-account?)
|
"recovered-account?" recovered-account?)
|
||||||
(rf/merge cofx
|
(rf/merge cofx
|
||||||
{:db (on-login-update-db db login-only? now)
|
{:db (on-login-update-db db now)
|
||||||
:json-rpc/call
|
:json-rpc/call
|
||||||
[{:method "web3_clientVersion"
|
[{:method "web3_clientVersion"
|
||||||
:on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]}
|
:on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]}
|
||||||
|
@ -636,151 +585,3 @@
|
||||||
(if login-only?
|
(if login-only?
|
||||||
(login-only-events key-uid password save-password?)
|
(login-only-events key-uid password save-password?)
|
||||||
(create-only-events recovered-account?)))))
|
(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]
|
[native-module.core :as native-module]
|
||||||
[status-im.notifications.core :as notifications]
|
[status-im.notifications.core :as notifications]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im.utils.keychain.core :as keychain]
|
|
||||||
[status-im.wallet.core :as wallet]
|
[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
|
(rf/defn logout-method
|
||||||
{:events [::logout-method]}
|
{:events [::logout-method]}
|
||||||
|
@ -21,12 +36,11 @@
|
||||||
::logout nil
|
::logout nil
|
||||||
::multiaccounts/webview-debug-changed false
|
::multiaccounts/webview-debug-changed false
|
||||||
:keychain/clear-user-password key-uid
|
:keychain/clear-user-password key-uid
|
||||||
:profile/get-profiles-overview #(re-frame/dispatch
|
:profile/get-profiles-overview #(rf/dispatch
|
||||||
[:profile/get-profiles-overview-success
|
[:profile/get-profiles-overview-success %])}
|
||||||
%])}
|
|
||||||
(keychain/save-auth-method key-uid auth-method)
|
(keychain/save-auth-method key-uid auth-method)
|
||||||
(wallet/clear-timeouts)
|
(wallet/clear-timeouts)
|
||||||
(init/initialize-app-db))))
|
(initialize-app-db))))
|
||||||
|
|
||||||
(rf/defn logout
|
(rf/defn logout
|
||||||
{:events [:logout :multiaccounts.logout.ui/logout-confirmed
|
{:events [:logout :multiaccounts.logout.ui/logout-confirmed
|
||||||
|
@ -49,17 +63,3 @@
|
||||||
:confirm-button-text (i18n/label :t/logout)
|
:confirm-button-text (i18n/label :t/logout)
|
||||||
:on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
|
:on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
|
||||||
:on-cancel nil}})
|
: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
|
(ns status-im.multiaccounts.recover.core
|
||||||
(:require [clojure.string :as string]
|
(:require [re-frame.core :as re-frame]
|
||||||
[re-frame.core :as re-frame]
|
|
||||||
[status-im.bottom-sheet.events :as bottom-sheet]
|
|
||||||
[status-im2.constants :as constants]
|
[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]
|
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
||||||
[native-module.core :as native-module]
|
[native-module.core :as native-module]
|
||||||
[status-im.popover.core :as popover]
|
|
||||||
[utils.re-frame :as rf]
|
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
[status-im2.navigation.events :as navigation]
|
[taoensso.timbre :as log]))
|
||||||
[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]}))
|
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
::import-multiaccount
|
::import-multiaccount
|
||||||
|
@ -110,68 +34,3 @@
|
||||||
(update derived-data constants/path-whisper-keyword assoc :name name)]
|
(update derived-data constants/path-whisper-keyword assoc :name name)]
|
||||||
(re-frame/dispatch [success-event root-data derived-data-extended]))))))))))))
|
(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]
|
[native-module.core :as native-module]
|
||||||
[status-im.popover.core :as popover]
|
[status-im.popover.core :as popover]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im.utils.keychain.core :as keychain]
|
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
[utils.security.core :as security]))
|
[utils.security.core :as security]))
|
||||||
|
|
||||||
|
@ -35,19 +34,13 @@
|
||||||
(rf/defn password-reset-success
|
(rf/defn password-reset-success
|
||||||
{:events [::password-reset-success]}
|
{:events [::password-reset-success]}
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [{:keys [key-uid]} (:profile/profile db)
|
(rf/merge cofx
|
||||||
auth-method (get db :auth-method keychain/auth-method-none)
|
{:db (dissoc
|
||||||
new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])]
|
db
|
||||||
(rf/merge cofx
|
:multiaccount/reset-password-form-vals
|
||||||
{:db (dissoc
|
:multiaccount/reset-password-errors
|
||||||
db
|
:multiaccount/reset-password-next-enabled?
|
||||||
:multiaccount/reset-password-form-vals
|
:multiaccount/resetting-password?)}))
|
||||||
: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)))))
|
|
||||||
|
|
||||||
(defn change-db-password-cb
|
(defn change-db-password-cb
|
||||||
[res]
|
[res]
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
(assoc-in [:syncing :pairing-status] :connected)
|
(assoc-in [:syncing :pairing-status] :connected)
|
||||||
|
|
||||||
received-account?
|
received-account?
|
||||||
(assoc-in [:syncing :profile/profile] multiaccount-data)
|
(assoc-in [:syncing :profile] multiaccount-data)
|
||||||
|
|
||||||
error-on-pairing?
|
error-on-pairing?
|
||||||
(assoc-in [:syncing :pairing-status] :error)
|
(assoc-in [:syncing :pairing-status] :error)
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
{:dispatch [:syncing/clear-states]}
|
{:dispatch [:syncing/clear-states]}
|
||||||
|
|
||||||
(and completed-pairing? receiver?)
|
(and completed-pairing? receiver?)
|
||||||
{:dispatch [:multiaccounts.login/local-paired-user]}
|
{:dispatch [:profile.login/local-paired-user]}
|
||||||
|
|
||||||
(and error-on-pairing? (some? error))
|
(and error-on-pairing? (some? error))
|
||||||
{:dispatch [:toasts/upsert
|
{: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
|
[react/view
|
||||||
{:style {:flex-direction :row}}
|
{:style {:flex-direction :row}}
|
||||||
[checkbox/checkbox
|
[checkbox/checkbox
|
||||||
{:checked? save-password?
|
{:checked? save-password?
|
||||||
:style {:margin-right 10}
|
:style {:margin-right 10}}]
|
||||||
:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
|
;; should be reimplemented
|
||||||
|
;;:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
|
||||||
[react/text (i18n/label :t/keycard-dont-ask-card)]])))
|
[react/text (i18n/label :t/keycard-dont-ask-card)]])))
|
||||||
|
|
||||||
(defn bezier-easing
|
(defn bezier-easing
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[status-im.keycard.login :as keycard.login]
|
[status-im.keycard.login :as keycard.login]
|
||||||
[status-im.multiaccounts.core :as multiaccounts]
|
[status-im.multiaccounts.core :as multiaccounts]
|
||||||
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
|
||||||
[status-im.react-native.resources :as resources]
|
[status-im.react-native.resources :as resources]
|
||||||
[status-im.ui.components.icons.icons :as icons]
|
[status-im.ui.components.icons.icons :as icons]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
|
@ -444,7 +443,6 @@
|
||||||
{:events [:multiaccounts.create.ui/get-new-key]}
|
{:events [:multiaccounts.create.ui/get-new-key]}
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(rf/merge cofx
|
(rf/merge cofx
|
||||||
(multiaccounts.create/prepare-intro-wizard)
|
|
||||||
(bottom-sheet/hide-bottom-sheet-old)
|
(bottom-sheet/hide-bottom-sheet-old)
|
||||||
(navigation/navigate-to :get-your-keys nil)))
|
(navigation/navigate-to :get-your-keys nil)))
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.ui.components.animation :as anim]
|
[status-im.ui.components.animation :as anim]
|
||||||
[status-im.ui.components.react :as react]
|
[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.frozen-card.view :as frozen-card]
|
||||||
[status-im.ui.screens.keycard.views :as keycard.views]
|
[status-im.ui.screens.keycard.views :as keycard.views]
|
||||||
[status-im.ui.screens.profile.user.views :as profile.user]
|
[status-im.ui.screens.profile.user.views :as profile.user]
|
||||||
|
@ -142,15 +141,6 @@
|
||||||
(= :share-chat-key view)
|
(= :share-chat-key view)
|
||||||
[profile.user/share-chat-key]
|
[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)
|
(= :transaction-data view)
|
||||||
[signing/transaction-data]
|
[signing/transaction-data]
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[status-im2.constants :as constants]
|
[status-im2.constants :as constants]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[status-im.multiaccounts.biometric.core :as biometric]
|
|
||||||
[status-im.multiaccounts.reset-password.core :as reset-password]
|
[status-im.multiaccounts.reset-password.core :as reset-password]
|
||||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||||
[status-im.ui.components.common.common :as components.common]
|
[status-im.ui.components.common.common :as components.common]
|
||||||
|
@ -34,9 +33,7 @@
|
||||||
profile-pictures-visibility]}
|
profile-pictures-visibility]}
|
||||||
[:profile/profile]
|
[:profile/profile]
|
||||||
has-picture [:profile/has-picture]
|
has-picture [:profile/has-picture]
|
||||||
supported-biometric-auth [:supported-biometric-auth]
|
|
||||||
keycard? [:keycard-multiaccount?]
|
keycard? [:keycard-multiaccount?]
|
||||||
auth-method [:auth-method]
|
|
||||||
profile-pictures-show-to [:multiaccount/profile-pictures-show-to]]
|
profile-pictures-show-to [:multiaccount/profile-pictures-show-to]]
|
||||||
[react/scroll-view {:padding-vertical 8}
|
[react/scroll-view {:padding-vertical 8}
|
||||||
[quo/list-header (i18n/label :t/security)]
|
[quo/list-header (i18n/label :t/security)]
|
||||||
|
@ -48,18 +45,6 @@
|
||||||
:chevron (boolean mnemonic)
|
:chevron (boolean mnemonic)
|
||||||
:accessory (when mnemonic [components.common/counter {:size 22} 1])
|
:accessory (when mnemonic [components.common/counter {:size 22} 1])
|
||||||
:on-press #(re-frame/dispatch [:navigate-to :backup-seed])}]
|
: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]
|
[separator]
|
||||||
[quo/list-header (i18n/label :t/privacy)]
|
[quo/list-header (i18n/label :t/privacy)]
|
||||||
[quo/list-item
|
[quo/list-item
|
||||||
|
|
|
@ -58,7 +58,6 @@
|
||||||
[status-im.ui.screens.rpc-usage-info :as rpc-usage-info]
|
[status-im.ui.screens.rpc-usage-info :as rpc-usage-info]
|
||||||
[status-im.ui.screens.stickers.views :as stickers]
|
[status-im.ui.screens.stickers.views :as stickers]
|
||||||
[status-im.ui.screens.sync-settings.views :as sync-settings]
|
[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.edit-node.views :as edit-wakuv2-node]
|
||||||
[status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings]
|
[status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings]
|
||||||
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
|
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
|
||||||
|
@ -349,9 +348,6 @@
|
||||||
:options {:topBar {:title {:text (i18n/label :t/principles)}}
|
:options {:topBar {:title {:text (i18n/label :t/principles)}}
|
||||||
:insets {:top? true}}
|
:insets {:top? true}}
|
||||||
:component about-app/principles}
|
:component about-app/principles}
|
||||||
{:name :force-accept-tos
|
|
||||||
:options {:insets {:top? true}}
|
|
||||||
:component terms-of-service/force-accept-tos}
|
|
||||||
{:name :manage-dapps-permissions
|
{:name :manage-dapps-permissions
|
||||||
;;TODO dynamic title
|
;;TODO dynamic title
|
||||||
:options {:insets {:top? true}}
|
: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
|
(ns status-im.utils.keychain.core
|
||||||
(:require ["react-native-keychain" :as react-native-keychain]
|
(:require [react-native.keychain :as keychain]
|
||||||
[clojure.string :as string]
|
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[native-module.core :as native-module]
|
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im.utils.platform :as platform]
|
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[utils.security.core :as security]))
|
[oops.core :as oops]))
|
||||||
|
|
||||||
(defn- check-conditions
|
(def auth-method-biometric "biometric")
|
||||||
[callback & checks]
|
(def auth-method-biometric-prepare "biometric-prepare")
|
||||||
(if (= (count checks) 0)
|
(def auth-method-none "none")
|
||||||
(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)))
|
|
||||||
|
|
||||||
(defn- whisper-key-name
|
(defn- whisper-key-name
|
||||||
[address]
|
[address]
|
||||||
(str address "-whisper"))
|
(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
|
(re-frame/reg-fx
|
||||||
:keychain/get-keycard-keys
|
:keychain/get-keycard-keys
|
||||||
(fn [[key-uid callback]]
|
(fn [[key-uid callback]]
|
||||||
(get-credentials
|
(keychain/get-credentials
|
||||||
key-uid
|
key-uid
|
||||||
(fn [^js encryption-key-data]
|
(fn [encryption-key-data]
|
||||||
(if encryption-key-data
|
(if encryption-key-data
|
||||||
(get-credentials
|
(keychain/get-credentials
|
||||||
(whisper-key-name key-uid)
|
(whisper-key-name key-uid)
|
||||||
(fn [^js whisper-key-data]
|
(fn [whisper-key-data]
|
||||||
(if whisper-key-data
|
(if whisper-key-data
|
||||||
(callback [(.-password encryption-key-data)
|
(callback [(oops/oget encryption-key-data "password")
|
||||||
(.-password whisper-key-data)])
|
(oops/oget whisper-key-data "password")])
|
||||||
(callback nil))))
|
(callback nil))))
|
||||||
(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
|
(re-frame/reg-fx
|
||||||
:keychain/save-keycard-keys
|
:keychain/save-keycard-keys
|
||||||
(fn [[key-uid encryption-public-key whisper-private-key]]
|
(fn [[key-uid encryption-public-key whisper-private-key]]
|
||||||
(save-credentials
|
(keychain/save-credentials
|
||||||
key-uid
|
key-uid
|
||||||
key-uid
|
key-uid
|
||||||
encryption-public-key
|
encryption-public-key
|
||||||
#(when-not %
|
#(when-not %
|
||||||
(log/error
|
(log/error
|
||||||
(str "Error while saving encryption-public-key"))))
|
(str "Error while saving encryption-public-key"))))
|
||||||
(save-credentials
|
(keychain/save-credentials
|
||||||
(whisper-key-name key-uid)
|
(whisper-key-name key-uid)
|
||||||
key-uid
|
key-uid
|
||||||
whisper-private-key
|
whisper-private-key
|
||||||
|
@ -215,19 +47,6 @@
|
||||||
(log/error
|
(log/error
|
||||||
(str "Error while saving whisper-private-key"))))))
|
(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
|
(rf/defn get-keycard-keys
|
||||||
[_ key-uid]
|
[_ key-uid]
|
||||||
{:keychain/get-keycard-keys
|
{:keychain/get-keycard-keys
|
||||||
|
@ -235,16 +54,8 @@
|
||||||
#(re-frame/dispatch
|
#(re-frame/dispatch
|
||||||
[:multiaccounts.login.callback/get-keycard-keys-success key-uid %])]})
|
[: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
|
(rf/defn save-keycard-keys
|
||||||
[_ key-uid encryption-public-key whisper-private-key]
|
[_ key-uid encryption-public-key whisper-private-key]
|
||||||
{:keychain/save-keycard-keys [key-uid
|
{:keychain/save-keycard-keys [key-uid
|
||||||
encryption-public-key
|
encryption-public-key
|
||||||
whisper-private-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]
|
(fn [json callback]
|
||||||
(callback (.multiAccountStoreDerivedAccounts native-status json)))
|
(callback (.multiAccountStoreDerivedAccounts native-status json)))
|
||||||
|
|
||||||
|
:getNodeConfig (fn [] (types/clj->json {:WakuV2Config ""}))
|
||||||
|
|
||||||
|
:backupDisabledDataDir (fn [] "./smth")
|
||||||
|
:keystoreDir (fn [] "")
|
||||||
|
:logFileDirectory (fn [] "./smth")
|
||||||
:clearCookies identity
|
:clearCookies identity
|
||||||
|
|
||||||
:clearStorageAPIs identity
|
:clearStorageAPIs identity
|
||||||
|
|
||||||
:setBlankPreviewFlag identity
|
:setBlankPreviewFlag identity
|
||||||
|
|
||||||
:callPrivateRPC
|
:callPrivateRPC
|
||||||
(fn [payload callback]
|
(fn [payload callback]
|
||||||
(callback (.callPrivateRPC native-status payload)))
|
(callback (.callPrivateRPC native-status payload)))
|
||||||
|
|
||||||
|
:createAccountAndLogin (fn [request] (.createAccountAndLogin native-status request))
|
||||||
|
|
||||||
:saveAccountAndLogin
|
:saveAccountAndLogin
|
||||||
(fn [multiaccount-data password settings config accounts-data]
|
(fn [multiaccount-data password settings config accounts-data]
|
||||||
(.saveAccountAndLogin native-status 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))}
|
(update-in [:wallet :accounts] dissoc deleted-address))}
|
||||||
(navigation/pop-to-root :shell-stack))))
|
(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
|
(rf/defn delete-account-key
|
||||||
{:events [:wallet.accounts/delete-key]}
|
{:events [:wallet.accounts/delete-key]}
|
||||||
[{:keys [db] :as cofx} account password on-error]
|
[{: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]
|
[react-native.safe-area :as safe-area]
|
||||||
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
|
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
|
||||||
[status-im2.contexts.onboarding.common.navigation-bar.view :as navigation-bar]
|
[status-im2.contexts.onboarding.common.navigation-bar.view :as navigation-bar]
|
||||||
[status-im.multiaccounts.biometric.core :as biometric]
|
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im2.common.resources :as resources]
|
[status-im2.common.resources :as resources]
|
||||||
[status-im2.common.parallax.view :as parallax]
|
[status-im2.common.parallax.view :as parallax]
|
||||||
[status-im2.contexts.onboarding.common.background.view :as background]
|
[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
|
(defn page-title
|
||||||
[]
|
[]
|
||||||
|
@ -24,9 +24,9 @@
|
||||||
|
|
||||||
(defn enable-biometrics-buttons
|
(defn enable-biometrics-buttons
|
||||||
[insets]
|
[insets]
|
||||||
(let [supported-biometric (rf/sub [:supported-biometric-auth])
|
(let [supported-biometric-type (rf/sub [:biometric/supported-type])
|
||||||
bio-type-label (biometric/get-label supported-biometric)
|
bio-type-label (biometric/get-label-by-type supported-biometric-type)
|
||||||
profile-color (:color (rf/sub [:onboarding-2/profile]))]
|
profile-color (:color (rf/sub [:onboarding-2/profile]))]
|
||||||
[rn/view {:style (style/buttons insets)}
|
[rn/view {:style (style/buttons insets)}
|
||||||
[quo/button
|
[quo/button
|
||||||
{:accessibility-label :enable-biometrics-button
|
{:accessibility-label :enable-biometrics-button
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
(ns status-im2.contexts.onboarding.events
|
(ns status-im2.contexts.onboarding.events
|
||||||
(:require
|
(:require [native-module.core :as native-module]
|
||||||
[clojure.string :as string]
|
[re-frame.core :as re-frame]
|
||||||
[native-module.core :as native-module]
|
[status-im.utils.types :as types]
|
||||||
[re-frame.core :as re-frame]
|
[status-im2.constants :as constants]
|
||||||
[status-im.ethereum.core :as ethereum]
|
[taoensso.timbre :as log]
|
||||||
[status-im.utils.types :as types]
|
[utils.i18n :as i18n]
|
||||||
[status-im2.config :as config]
|
[utils.re-frame :as rf]
|
||||||
[status-im2.constants :as constants]
|
[utils.security.core :as security]
|
||||||
[taoensso.timbre :as log]
|
[status-im2.contexts.profile.create.events :as profile.create]
|
||||||
[utils.i18n :as i18n]
|
[status-im2.contexts.profile.recover.events :as profile.recover]
|
||||||
[utils.re-frame :as rf]
|
[status-im2.common.biometric.events :as biometric]))
|
||||||
[utils.security.core :as security]))
|
|
||||||
|
|
||||||
(re-frame/reg-fx
|
|
||||||
:multiaccount/create-account-and-login
|
|
||||||
(fn [request]
|
|
||||||
(native-module/create-account-and-login request)))
|
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:multiaccount/validate-mnemonic
|
:multiaccount/validate-mnemonic
|
||||||
|
@ -28,11 +22,6 @@
|
||||||
(when on-error (on-error error))
|
(when on-error (on-error error))
|
||||||
(on-success mnemonic keyUID)))))))
|
(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
|
(rf/defn profile-data-set
|
||||||
{:events [:onboarding-2/profile-data-set]}
|
{:events [:onboarding-2/profile-data-set]}
|
||||||
[{:keys [db]} onboarding-data]
|
[{:keys [db]} onboarding-data]
|
||||||
|
@ -42,82 +31,34 @@
|
||||||
(rf/defn enable-biometrics
|
(rf/defn enable-biometrics
|
||||||
{:events [:onboarding-2/enable-biometrics]}
|
{:events [:onboarding-2/enable-biometrics]}
|
||||||
[_]
|
[_]
|
||||||
{:biometric-auth/authenticate [#(rf/dispatch [:onboarding-2/biometrics-done %]) {}]})
|
{:biometric/authenticate {:on-success #(rf/dispatch [:onboarding-2/biometrics-done])
|
||||||
|
:on-fail #(rf/dispatch [:onboarding-2/biometrics-fail %])}})
|
||||||
(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}})))
|
|
||||||
|
|
||||||
(rf/defn biometrics-done
|
(rf/defn biometrics-done
|
||||||
{:events [:onboarding-2/biometrics-done]}
|
{:events [:onboarding-2/biometrics-done]}
|
||||||
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
[{:keys [db]}]
|
||||||
(if bioauth-success
|
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
|
||||||
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
|
:dispatch [:onboarding-2/create-account-and-login]})
|
||||||
:dispatch [:onboarding-2/create-account-and-login]}
|
|
||||||
(show-biometrics-message cofx bioauth-message bioauth-code)))
|
|
||||||
|
|
||||||
(defn strip-file-prefix
|
(rf/defn biometrics-fail
|
||||||
[path]
|
{:events [:onboarding-2/biometrics-fail]}
|
||||||
(when path
|
[cofx code]
|
||||||
(string/replace-first path "file://" "")))
|
(biometric/show-message cofx code))
|
||||||
|
|
||||||
(rf/defn create-account-and-login
|
(rf/defn create-account-and-login
|
||||||
{:events [:onboarding-2/create-account-and-login]}
|
{:events [:onboarding-2/create-account-and-login]}
|
||||||
[{:keys [db]}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [{:keys [display-name
|
(let [{:keys [display-name seed-phrase password image-path color] :as profile}
|
||||||
seed-phrase
|
(:onboarding-2/profile db)]
|
||||||
password
|
(rf/merge cofx
|
||||||
image-path
|
{:dispatch [:navigate-to :generating-keys]
|
||||||
color]} (:onboarding-2/profile db)
|
:db (-> db
|
||||||
log-enabled? (boolean (not-empty config/log-level))
|
(dissoc :profile/login)
|
||||||
effect (if seed-phrase
|
(dissoc :auth-method)
|
||||||
:multiaccount/restore-account-and-login
|
(assoc :onboarding-2/new-account? true))}
|
||||||
:multiaccount/create-account-and-login)
|
(if seed-phrase
|
||||||
request {:displayName display-name
|
(profile.recover/recover-profile-and-login profile)
|
||||||
:deviceName (native-module/get-installation-name)
|
(profile.create/create-profile-and-login profile)))))
|
||||||
: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))}))
|
|
||||||
|
|
||||||
(rf/defn on-delete-profile-success
|
(rf/defn on-delete-profile-success
|
||||||
{:events [:onboarding-2/on-delete-profile-success]}
|
{:events [:onboarding-2/on-delete-profile-success]}
|
||||||
|
@ -173,13 +114,11 @@
|
||||||
[{:keys [db]}]
|
[{:keys [db]}]
|
||||||
(let [masked-password (get-in db [:onboarding-2/profile :password])
|
(let [masked-password (get-in db [:onboarding-2/profile :password])
|
||||||
key-uid (get-in db [:profile/profile :key-uid])
|
key-uid (get-in db [:profile/profile :key-uid])
|
||||||
biometric-enabled? (=
|
biometric-enabled? (= (get-in db [:onboarding-2/profile :auth-method])
|
||||||
constants/auth-method-biometric
|
constants/auth-method-biometric)]
|
||||||
(get-in db [:onboarding-2/profile :auth-method]))]
|
|
||||||
|
|
||||||
(cond-> {:dispatch [:navigate-to :identifiers]}
|
(cond-> {:dispatch [:navigate-to :identifiers]}
|
||||||
biometric-enabled?
|
biometric-enabled?
|
||||||
(assoc :biometric/enable-and-save-password
|
(assoc :keychain/save-password-and-auth-method
|
||||||
{:key-uid key-uid
|
{:key-uid key-uid
|
||||||
:masked-password masked-password
|
:masked-password masked-password
|
||||||
:on-success #(log/debug "successfully saved biometric")
|
:on-success #(log/debug "successfully saved biometric")
|
||||||
|
|
|
@ -18,18 +18,13 @@
|
||||||
(reset! scan-sync-code/dismiss-animations reset-top-animation-fn))
|
(reset! scan-sync-code/dismiss-animations reset-top-animation-fn))
|
||||||
:animations-duration constants/onboarding-modal-animation-duration
|
:animations-duration constants/onboarding-modal-animation-duration
|
||||||
:animations-delay constants/onboarding-modal-animation-delay
|
:animations-delay constants/onboarding-modal-animation-delay
|
||||||
:top-card {:on-press (fn []
|
:top-card {:on-press #(debounce/dispatch-and-chill [:open-modal
|
||||||
(debounce/dispatch-and-chill [:open-modal
|
:sign-in-intro]
|
||||||
:sign-in-intro]
|
2000)
|
||||||
2000)
|
|
||||||
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
|
|
||||||
:heading (i18n/label :t/sign-in)
|
:heading (i18n/label :t/sign-in)
|
||||||
:animated-heading (i18n/label :t/sign-in-by-syncing)
|
:animated-heading (i18n/label :t/sign-in-by-syncing)
|
||||||
:accessibility-label :already-use-status-button}
|
:accessibility-label :already-use-status-button}
|
||||||
:bottom-card {:on-press (fn []
|
:bottom-card {:on-press #(rf/dispatch [:navigate-to :new-to-status])
|
||||||
(rf/dispatch [:navigate-to :new-to-status])
|
|
||||||
(rf/dispatch
|
|
||||||
[:hide-terms-of-services-opt-in-screen]))
|
|
||||||
:heading (i18n/label :t/new-to-status)
|
:heading (i18n/label :t/new-to-status)
|
||||||
:accessibility-label :new-to-status-button}}
|
:accessibility-label :new-to-status-button}}
|
||||||
[quo/text
|
[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
|
(ns status-im2.contexts.profile.events
|
||||||
(:require [utils.re-frame :as rf]
|
(: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
|
(rf/defn profile-selected
|
||||||
{:events [:profile/profile-selected]}
|
{:events [:profile/profile-selected]}
|
||||||
|
@ -11,6 +20,12 @@
|
||||||
(assoc :key-uid key-uid)
|
(assoc :key-uid key-uid)
|
||||||
(dissoc :error :password)))})
|
(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
|
(defn rpc->profiles-overview
|
||||||
[{:keys [customizationColor keycard-pairing] :as profile}]
|
[{:keys [customizationColor keycard-pairing] :as profile}]
|
||||||
(-> profile
|
(-> profile
|
||||||
|
@ -18,18 +33,24 @@
|
||||||
(assoc :customization-color (keyword customizationColor))
|
(assoc :customization-color (keyword customizationColor))
|
||||||
(assoc :keycard-pairing (when-not (string/blank? keycard-pairing) keycard-pairing))))
|
(assoc :keycard-pairing (when-not (string/blank? keycard-pairing) keycard-pairing))))
|
||||||
|
|
||||||
(rf/defn init-profiles-overview
|
(defn reduce-profiles
|
||||||
[{:keys [db] :as cofx} profiles]
|
[profiles]
|
||||||
(let [profiles (reduce
|
(reduce
|
||||||
(fn [acc {:keys [key-uid] :as profile}]
|
(fn [acc {:keys [key-uid] :as profile}]
|
||||||
(assoc acc key-uid (rpc->profiles-overview profile)))
|
(assoc acc key-uid (rpc->profiles-overview profile)))
|
||||||
{}
|
{}
|
||||||
profiles)
|
profiles))
|
||||||
{:keys [key-uid]} (first (sort-by :timestamp > (vals profiles)))]
|
|
||||||
(rf/merge cofx
|
(rf/defn get-profiles-overview-success
|
||||||
{:db (assoc db :profile/profiles-overview profiles)
|
{:events [:profile/get-profiles-overview-success]}
|
||||||
:keychain/get-auth-method
|
[cofx profiles-overview]
|
||||||
[key-uid #(rf/dispatch [:multiaccounts.login/get-auth-method-success % key-uid])]
|
(if (seq profiles-overview)
|
||||||
:dispatch-n [[:get-opted-in-to-new-terms-of-service]
|
(let [profiles (reduce-profiles profiles-overview)
|
||||||
[:load-information-box-states]]}
|
{:keys [key-uid]} (first (sort-by :timestamp > (vals profiles)))]
|
||||||
(profile-selected key-uid))))
|
(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]))
|
(:require [quo2.foundations.colors :as colors]))
|
||||||
|
|
||||||
;; Profiles Section
|
;; 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]
|
(:require [native-module.core :as native-module]
|
||||||
[quo2.core :as quo]
|
[quo2.core :as quo]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im2.common.confirmation-drawer.view :as confirmation-drawer]
|
[status-im2.common.confirmation-drawer.view :as confirmation-drawer]
|
||||||
[status-im2.contexts.onboarding.common.background.view :as background]
|
[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]
|
[taoensso.timbre :as log]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
|
@ -15,10 +15,6 @@
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
[clojure.string :as string]))
|
[clojure.string :as string]))
|
||||||
|
|
||||||
(defn login-multiaccount
|
|
||||||
[]
|
|
||||||
(rf/dispatch [:multiaccounts.login.ui/password-input-submitted]))
|
|
||||||
|
|
||||||
(defn new-account-options
|
(defn new-account-options
|
||||||
[]
|
[]
|
||||||
[quo/action-drawer
|
[quo/action-drawer
|
||||||
|
@ -76,8 +72,10 @@
|
||||||
:shell? true}]))
|
:shell? true}]))
|
||||||
|
|
||||||
(defn profile-card
|
(defn profile-card
|
||||||
[{:keys [name key-uid customization-color keycard-pairing last-index set-hide-profiles]}
|
[{:keys [name key-uid customization-color keycard-pairing]}
|
||||||
index]
|
index
|
||||||
|
_
|
||||||
|
{:keys [last-index set-hide-profiles]}]
|
||||||
(let [last-item? (= last-index index)
|
(let [last-item? (= last-index index)
|
||||||
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])]
|
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])]
|
||||||
[quo/profile-card
|
[quo/profile-card
|
||||||
|
@ -101,11 +99,7 @@
|
||||||
|
|
||||||
(defn profiles-section
|
(defn profiles-section
|
||||||
[{:keys [set-hide-profiles]}]
|
[{:keys [set-hide-profiles]}]
|
||||||
(let [multiaccounts (vals (rf/sub [:profile/profiles-overview]))
|
(let [profiles (vals (rf/sub [:profile/profiles-overview]))]
|
||||||
profiles-data (map #(assoc %
|
|
||||||
:last-index (- (count multiaccounts) 1)
|
|
||||||
:set-hide-profiles set-hide-profiles)
|
|
||||||
multiaccounts)]
|
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style style/profiles-container}
|
{:style style/profiles-container}
|
||||||
[rn/view
|
[rn/view
|
||||||
|
@ -123,9 +117,11 @@
|
||||||
:accessibility-label :show-new-account-options}
|
:accessibility-label :show-new-account-options}
|
||||||
:main-icons/add]]
|
:main-icons/add]]
|
||||||
[rn/flat-list
|
[rn/flat-list
|
||||||
{:data (sort-by :timestamp > profiles-data)
|
{:data (sort-by :timestamp > profiles)
|
||||||
:key-fn :key-uid
|
:key-fn :key-uid
|
||||||
:content-container-style {:padding-bottom 20}
|
:content-container-style {:padding-bottom 20}
|
||||||
|
:render-data {:last-index (- (count profiles) 1)
|
||||||
|
:set-hide-profiles set-hide-profiles}
|
||||||
:render-fn profile-card}]]))
|
:render-fn profile-card}]]))
|
||||||
|
|
||||||
(defn forget-password-doc
|
(defn forget-password-doc
|
||||||
|
@ -175,19 +171,23 @@
|
||||||
[quo/text {:size :paragraph-2}
|
[quo/text {:size :paragraph-2}
|
||||||
(i18n/label :t/forgot-your-password-info-create-new-password-description)]]]]])
|
(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
|
(defn login-section
|
||||||
[{:keys [set-show-profiles]}]
|
[{:keys [set-show-profiles]}]
|
||||||
(let [{:keys [error processing password]} (rf/sub [:profile/login])
|
(let [{:keys [error processing password]} (rf/sub [:profile/login])
|
||||||
{:keys [key-uid name customization-color]} (rf/sub [:profile/login-profile])
|
{:keys [key-uid name customization-color]} (rf/sub [:profile/login-profile])
|
||||||
sign-in-enabled? (rf/sub [:sign-in-enabled?])
|
sign-in-enabled? (rf/sub [:sign-in-enabled?])
|
||||||
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])
|
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])
|
||||||
error (if (and (some? error)
|
error (get-error-message error)
|
||||||
(or (= error "file is not a database")
|
login-multiaccount #(rf/dispatch [:profile.login/login])]
|
||||||
(string/starts-with? error
|
|
||||||
"failed to set ")
|
|
||||||
(string/starts-with? error "Failed")))
|
|
||||||
(i18n/label :t/oops-wrong-password)
|
|
||||||
error)]
|
|
||||||
[rn/keyboard-avoiding-view
|
[rn/keyboard-avoiding-view
|
||||||
{:style style/login-container
|
{:style style/login-container
|
||||||
:keyboardVerticalOffset (- (safe-area/get-bottom))}
|
:keyboardVerticalOffset (- (safe-area/get-bottom))}
|
||||||
|
@ -257,7 +257,7 @@
|
||||||
:style {:margin-bottom (+ (safe-area/get-bottom) 12)}}
|
:style {:margin-bottom (+ (safe-area/get-bottom) 12)}}
|
||||||
(i18n/label :t/log-in)]]))
|
(i18n/label :t/log-in)]]))
|
||||||
|
|
||||||
(defn views
|
(defn view
|
||||||
[]
|
[]
|
||||||
(let [show-profiles? (reagent/atom false)
|
(let [show-profiles? (reagent/atom false)
|
||||||
set-show-profiles #(reset! show-profiles? true)
|
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
|
(ns status-im2.events
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [status-im2.common.json-rpc.events]
|
||||||
[status-im2.common.json-rpc.events]
|
|
||||||
[status-im2.common.toasts.events]
|
[status-im2.common.toasts.events]
|
||||||
[status-im2.contexts.add-new-contact.events]
|
[status-im2.contexts.add-new-contact.events]
|
||||||
status-im2.contexts.onboarding.events
|
status-im2.contexts.onboarding.events
|
||||||
[status-im.bottom-sheet.events]
|
[status-im.bottom-sheet.events]
|
||||||
[status-im2.db :as db]
|
[status-im2.db :as db]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.datetime :as datetime]
|
|
||||||
status-im2.contexts.shell.share.events
|
status-im2.contexts.shell.share.events
|
||||||
status-im2.contexts.syncing.events
|
status-im2.contexts.syncing.events
|
||||||
status-im2.contexts.chat.events
|
status-im2.contexts.chat.events
|
||||||
status-im2.common.password-authentication.events
|
status-im2.common.password-authentication.events
|
||||||
status-im2.contexts.communities.overview.events
|
status-im2.contexts.communities.overview.events
|
||||||
status-im2.contexts.chat.photo-selector.events
|
status-im2.contexts.chat.photo-selector.events
|
||||||
[status-im2.contexts.profile.events :as profile.events]
|
|
||||||
status-im2.common.theme.events
|
status-im2.common.theme.events
|
||||||
[status-im2.navigation.events :as navigation]
|
status-im2.contexts.profile.events
|
||||||
[native-module.core :as native-module]))
|
[status-im.keycard.core :as keycard]))
|
||||||
|
|
||||||
(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)))
|
|
||||||
|
|
||||||
(rf/defn start-app
|
(rf/defn start-app
|
||||||
{:events [:app-started]}
|
{:events [:app-started]}
|
||||||
[cofx]
|
[cofx]
|
||||||
(rf/merge cofx
|
(rf/merge cofx
|
||||||
{:theme/init-theme nil
|
{:db db/app-db
|
||||||
:biometric/get-supported-biometric-auth nil
|
:theme/init-theme nil
|
||||||
:network/listen-to-network-info nil
|
:network/listen-to-network-info nil
|
||||||
:keycard/register-card-events nil
|
:biometric/get-supported-biometric-type nil
|
||||||
:keycard/check-nfc-support nil
|
;;app starting flow continues in get-profiles-overview
|
||||||
:keycard/check-nfc-enabled nil
|
:profile/get-profiles-overview #(rf/dispatch
|
||||||
:keycard/retrieve-pairings nil
|
|
||||||
:profile/get-profiles-overview #(re-frame/dispatch
|
|
||||||
[:profile/get-profiles-overview-success %])}
|
[:profile/get-profiles-overview-success %])}
|
||||||
(initialize-app-db)))
|
(keycard/init)))
|
||||||
|
|
|
@ -64,15 +64,6 @@
|
||||||
{:root {:stack {:children [{:component {:name :onboarding-notification
|
{:root {:stack {:children [{:component {:name :onboarding-notification
|
||||||
:id :onboarding-notification
|
:id :onboarding-notification
|
||||||
:options (options/statusbar-and-navbar-root)}}]
|
: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 (merge (options/default-root)
|
||||||
(options/statusbar-and-navbar-root)
|
(options/statusbar-and-navbar-root)
|
||||||
{:topBar (assoc (options/topbar-options) :visible false)})}}}})
|
{: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.sign-in.view :as sign-in]
|
||||||
[status-im2.contexts.onboarding.generating-keys.view :as generating-keys]
|
[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.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.quo-preview.main :as quo.preview]
|
||||||
[status-im2.contexts.shell.jump-to.view :as shell]
|
[status-im2.contexts.shell.jump-to.view :as shell]
|
||||||
[status-im2.contexts.syncing.scan-sync-code-page.view :as scan-sync-code-page]
|
[status-im2.contexts.syncing.scan-sync-code-page.view :as scan-sync-code-page]
|
||||||
|
@ -143,7 +143,7 @@
|
||||||
{:name :profiles
|
{:name :profiles
|
||||||
:options {:theme :dark
|
:options {:theme :dark
|
||||||
:layout options/onboarding-layout}
|
:layout options/onboarding-layout}
|
||||||
:component profiles/views}
|
:component profiles/view}
|
||||||
|
|
||||||
{:name :new-to-status
|
{:name :new-to-status
|
||||||
:options {:theme :dark
|
:options {:theme :dark
|
||||||
|
|
|
@ -228,12 +228,6 @@
|
||||||
(fn [network]
|
(fn [network]
|
||||||
(tokens/native-currency 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
|
(re-frame/reg-sub
|
||||||
:connectivity/state
|
:connectivity/state
|
||||||
:<- [:network-status]
|
:<- [: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]}]
|
(fn [{:keys [customization-color]}]
|
||||||
(or customization-color constants/profile-default-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
|
(re-frame/reg-sub
|
||||||
:multiaccount/public-key
|
:multiaccount/public-key
|
||||||
:<- [:profile/profile]
|
:<- [:profile/profile]
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
status-im2.subs.mailservers
|
status-im2.subs.mailservers
|
||||||
status-im2.subs.profile
|
status-im2.subs.profile
|
||||||
status-im2.subs.networks
|
status-im2.subs.networks
|
||||||
status-im2.subs.onboarding
|
|
||||||
status-im2.subs.pairing
|
status-im2.subs.pairing
|
||||||
status-im2.subs.search
|
status-im2.subs.search
|
||||||
status-im2.subs.shell
|
status-im2.subs.shell
|
||||||
|
@ -66,7 +65,7 @@
|
||||||
(reg-root-key-sub :networks/manage :networks/manage)
|
(reg-root-key-sub :networks/manage :networks/manage)
|
||||||
(reg-root-key-sub :get-pairing-installations :pairing/installations)
|
(reg-root-key-sub :get-pairing-installations :pairing/installations)
|
||||||
(reg-root-key-sub :tooltips :tooltips)
|
(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 :connectivity/ui-status-properties :connectivity/ui-status-properties)
|
||||||
(reg-root-key-sub :logged-in-since :logged-in-since)
|
(reg-root-key-sub :logged-in-since :logged-in-since)
|
||||||
(reg-root-key-sub :app-state :app-state)
|
(reg-root-key-sub :app-state :app-state)
|
||||||
|
@ -231,8 +230,6 @@
|
||||||
|
|
||||||
(reg-root-key-sub :auth-method :auth-method)
|
(reg-root-key-sub :auth-method :auth-method)
|
||||||
|
|
||||||
(reg-root-key-sub :tos-accept-next-root :tos-accept-next-root)
|
|
||||||
|
|
||||||
;; keycard
|
;; keycard
|
||||||
(reg-root-key-sub :keycard/banner-hidden :keycard/banner-hidden)
|
(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 :messenger/started? :messenger/started?)
|
||||||
|
|
||||||
(reg-root-key-sub :information-box-states :information-box-states)
|
|
||||||
|
|
||||||
; Messages home view -> tabs
|
; Messages home view -> tabs
|
||||||
(reg-root-key-sub :messages-home/selected-tab :messages-home/selected-tab)
|
(reg-root-key-sub :messages-home/selected-tab :messages-home/selected-tab)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
(ns status-im2.subs.subs-test
|
(ns status-im2.subs.subs-test
|
||||||
(:require [cljs.test :refer [deftest is testing]]
|
(:require [cljs.test :refer [deftest is testing]]
|
||||||
[status-im2.subs.onboarding :as onboarding]
|
|
||||||
[status-im2.subs.wallet.transactions :as wallet.transactions]))
|
[status-im2.subs.wallet.transactions :as wallet.transactions]))
|
||||||
|
|
||||||
(def transactions
|
(def transactions
|
||||||
|
@ -23,20 +22,3 @@
|
||||||
(testing "Check if transactions are sorted by date"
|
(testing "Check if transactions are sorted by date"
|
||||||
(is (= (wallet.transactions/group-transactions-by-date transactions)
|
(is (= (wallet.transactions/group-transactions-by-date transactions)
|
||||||
grouped-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-macros utils.re-frame)
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[re-frame.interceptor :as interceptor]
|
[re-frame.interceptor :as interceptor]
|
||||||
[taoensso.timbre :as log])
|
[taoensso.timbre :as log]
|
||||||
|
[utils.datetime :as datetime])
|
||||||
(:refer-clojure :exclude [merge reduce]))
|
(:refer-clojure :exclude [merge reduce]))
|
||||||
|
|
||||||
(def handler-nesting-level (atom 0))
|
(def handler-nesting-level (atom 0))
|
||||||
|
|
||||||
|
(re-frame/reg-cofx :now (fn [coeffects _] (assoc coeffects :now (datetime/timestamp))))
|
||||||
|
|
||||||
(def debug-handlers-names
|
(def debug-handlers-names
|
||||||
"Interceptor which logs debug information to js/console for each event."
|
"Interceptor which logs debug information to js/console for each event."
|
||||||
(interceptor/->interceptor
|
(interceptor/->interceptor
|
||||||
|
|
Loading…
Reference in New Issue