[#10195] Delete multiaccount

This commit is contained in:
Roman Volosovskyi 2020-07-14 16:33:59 +03:00
parent 4254dbcb26
commit 10a814764d
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
14 changed files with 269 additions and 18 deletions

View File

@ -504,10 +504,17 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
public String getKeyStorePath(String keyUID) {
final String commonKeydir = pathCombine(this.getNoBackupDirectory(), "/keystore");
final String keydir = pathCombine(commonKeydir, keyUID);
return keydir;
}
public void migrateKeyStoreDir(final String accountData, final String password) {
try {
final String commonKeydir = pathCombine(this.getNoBackupDirectory(), "/keystore");
final String keydir = pathCombine(commonKeydir, getKeyUID(accountData));
final String keydir = this.getKeyStorePath(this.getKeyUID(accountData));
Log.d(TAG, "before migrateKeyStoreDir " + keydir);
File keydirFile = new File(keydir);
@ -1183,6 +1190,27 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void deleteMultiaccount(final String keyUID, final Callback callback) {
Log.d(TAG, "deleteMultiaccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
final String keyStoreDir = this.getKeyStorePath(keyUID);
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.deleteMultiaccount(keyUID, keyStoreDir);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String generateAlias(final String seed) {
return Statusgo.generateAlias(seed);

View File

@ -221,6 +221,17 @@ RCT_EXPORT_METHOD(chaosModeUpdate:(BOOL)on
#endif
}
//////////////////////////////////////////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountImportPrivateKey() method called");
#endif
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
NSString *result = StatusgoDeleteMultiaccount(keyUID, multiaccountKeystoreDir.path);
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountGenerateAndDeriveAddresses
RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
@ -440,6 +451,18 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
NSLog(@"%@", result);
}
- (NSURL *) getKeyStoreDir:(NSString *)keyUID {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *rootUrl =[[fileManager
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
lastObject];
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSURL *multiaccountKeystoreDir = [oldKeystoreDir URLByAppendingPathComponent:keyUID];
return multiaccountKeystoreDir;
}
- (void) migrateKeystore:(NSString *)accountData
password:(NSString *)password {
NSFileManager *fileManager = [NSFileManager defaultManager];
@ -449,7 +472,7 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
NSString *keyUID = [self getKeyUID:accountData];
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSURL *multiaccountKeystoreDir = [oldKeystoreDir URLByAppendingPathComponent:keyUID];
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
NSArray *keys = [fileManager contentsOfDirectoryAtPath:multiaccountKeystoreDir.path error:nil];
if (keys.count == 0) {

View File

@ -0,0 +1,12 @@
(ns quo.components.separator
(:require [quo.react-native :as react]
[quo.design-system.colors :as colors]))
(defn separator [{:keys [color style]}]
[react/view
{:style
(merge
style
{:background-color (colors/get-color (or color :ui-01))
:align-self :stretch
:height 1})}])

View File

@ -10,7 +10,9 @@
[quo.components.list.footer :as list-footer]
[quo.components.list.item :as list-item]
[quo.components.controls.view :as controls]
[quo.components.bottom-sheet.view :as bottom-sheet]))
[quo.components.bottom-sheet.view :as bottom-sheet]
[quo.components.separator :as separator]
[quo.design-system.colors :as colors]))
(def text text/text)
(def header header/header)
@ -28,3 +30,5 @@
(def safe-area-provider safe-area/provider)
(def safe-area-consumer safe-area/consumer)
(def safe-area-view safe-area/view)
(def separator separator/separator)
(def get-color colors/get-color)

View File

@ -71,3 +71,6 @@
:backdrop "rgba(0,0,0,0.4)"})
(def theme (reagent/atom light-theme))
(defn get-color [color]
(get @theme color))

View File

@ -65,7 +65,8 @@
status-im.utils.universal-links.events
status-im.search.core
status-im.ui.screens.profile.events
status-im.chat.models.images))
status-im.chat.models.images
status-im.ui.screens.privacy-and-security-settings.events))
;; init module
(handlers/register-handler-fx

View File

@ -357,3 +357,10 @@
[mnemonic callback]
(log/debug "[native-module] validate-mnemonic")
(.validateMnemonic ^js (status) mnemonic callback))
(defn delete-multiaccount
"Delete multiaccount from database, deletes multiaccount's database and
key files."
[key-uid callback]
(log/debug "[native-module] delete-multiaccount")
(.deleteMultiaccount ^js (status) key-uid callback))

View File

@ -188,6 +188,9 @@
;; keycard
(reg-root-key-sub :keycard/new-account-sheet? :keycard/new-account-sheet?)
;; delete profile
(reg-root-key-sub :delete-profile/error :delete-profile/error)
;;GENERAL ==============================================================================================================
(re-frame/reg-sub

View File

@ -0,0 +1,86 @@
(ns status-im.ui.screens.privacy-and-security-settings.delete-profile
(:require [status-im.ui.components.react :as react]
[quo.core :as quo]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[re-frame.core :as re-frame]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.components.styles :as components.styles]
[status-im.i18n :as i18n]
[reagent.core :as reagent]
[status-im.utils.security :as security]
[status-im.ui.screens.privacy-and-security-settings.events :as delete-profile]))
(defn valid-password? [password]
(>= (count password) 6))
(defn keycard-pin []
#_(let [pin @(re-frame/subscribe [:keycard/pin])
step @(re-frame/subscribe [:keycard/pin-enter-step])
status @(re-frame/subscribe [:keycard/pin-status])
pin-retry-counter @(re-frame/subscribe [:keycard/pin-retry-counter])
puk-retry-counter @(re-frame/subscribe [:keycard/puk-retry-counter])
error-label @(re-frame/subscribe [:keycard/pin-error-label])]
[pin.views/pin-view
{:pin pin
:status status
:retry-counter pin-retry-counter
:error-label error-label
:step :current}]))
(defn delete-profile []
(let [password (reagent/atom nil)
text-input-ref (atom nil)]
(fn []
(let [keycard? @(re-frame/subscribe [:keycard-multiaccount?])
multiaccount @(re-frame/subscribe [:multiaccount])
error @(re-frame/subscribe [:delete-profile/error])]
(when (and @text-input-ref error (not @password))
(.clear ^js @text-input-ref))
[react/view components.styles/flex
[topbar/topbar {:modal? true}]
[react/view
{:style {:flex 1
:justify-content :space-between}}
[react/scroll-view {:style {:flex 1}}
[react/view {:style {:align-items :center}}
[quo/text {:weight :bold
:size :x-large}
(i18n/label :t/delete-profile)]]
[quo/list-item
{:title (multiaccounts/displayed-name multiaccount)
:icon [chat-icon.screen/contact-icon-contacts-tab
(multiaccounts/displayed-photo multiaccount)]}]
[quo/text {:style {:margin-horizontal 24}
:align :center
:color :negative}
(i18n/label :t/delete-profile-warning)]
(if keycard?
[keycard-pin]
[quo/text-input
{:style {:margin-horizontal 36
:margin-top 36}
:show-cancel false
:secure-text-entry true
:return-key-type :next
:on-submit-editing nil
:auto-focus true
:on-change-text #(reset! password (security/mask-data %))
:bottom-value 36
:get-ref #(reset! text-input-ref %)
:error (when (and error (not @password))
(if (= :wrong-password error)
(i18n/label :t/wrong-password)
(str error)))}])]
(when-not keycard?
[react/view {:style {:align-items :center}}
[quo/separator]
[react/view
{:style {:margin-vertical 8}}
[quo/button {:on-press #(do
(re-frame/dispatch
[::delete-profile/delete-profile @password])
(reset! password nil))
:theme :negative
:disabled ((complement valid-password?) @password)}
(i18n/label :t/delete-profile)]]])]]))))

View File

@ -0,0 +1,67 @@
(ns status-im.ui.screens.privacy-and-security-settings.events
(:require [status-im.utils.fx :as fx]
[re-frame.core :as re-frame]
[status-im.utils.security :as security]
[status-im.native-module.core :as status]
[status-im.ethereum.core :as ethereum]
[status-im.utils.types :as types]
[taoensso.timbre :as log]
[clojure.string :as clojure.string]
[status-im.i18n :as i18n]))
(defn safe-blank? [s]
(or (not s)
(clojure.string/blank? s)))
(re-frame/reg-fx
::delete-profile
(fn [{:keys [address key-uid callback masked-password]}]
(let [hashed-password
(-> masked-password
security/safe-unmask-data
ethereum/sha3)]
(status/verify
address
hashed-password
(fn [result]
(let [{:keys [error]} (types/json->clj result)]
(log/info "[delete-profile] verify-password" result error)
(if-not (safe-blank? error)
(callback :wrong-password nil)
(status/delete-multiaccount
key-uid
(fn [result]
(let [{:keys [error]} (types/json->clj result)]
(callback error nil)))))))))))
(fx/defn delete-profile
{:events [::delete-profile]}
[{:keys [db] :as cofx} masked-password]
(log/info "[delete-profile] delete")
(let [{:keys [key-uid wallet-root-address]} (:multiaccount db)]
{:db (dissoc db :delete-profile/error)
::delete-profile
{:masked-password masked-password
:key-uid key-uid
:address wallet-root-address
:callback
(fn [error result]
(log/info "[delete-profile] callback" error)
(if (safe-blank? error)
(re-frame/dispatch [::on-delete-profile-success result])
(re-frame/dispatch [::on-delete-profile-failure error])))}}))
(fx/defn on-delete-profile-success
{:events [::on-delete-profile-success]}
[cofx]
(log/info "[delete-profile] on-success")
{:utils/show-popup
{:title (i18n/label :t/profile-deleted-title)
:content (i18n/label :t/profile-deleted-content)
:on-dismiss #(re-frame/dispatch [:logout])}})
(fx/defn on-delete-profile-failure
{:events [::on-delete-profile-failure]}
[{:keys [db]} error]
(log/info "[delete-profile] on-failure" error)
{:db (assoc db :delete-profile/error error)})

View File

@ -10,10 +10,14 @@
[status-im.utils.platform :as platform])
(:require-macros [status-im.utils.views :as views]))
(defn separator []
[quo/separator {:style {:margin-vertical 8}}])
(views/defview privacy-and-security []
(views/letsubs [{:keys [mnemonic preview-privacy?]} [:multiaccount]
supported-biometric-auth [:supported-biometric-auth]
auth-method [:auth-method]]
auth-method [:auth-method]
keycard? [:keycard-multiaccount?]]
[react/view {:flex 1 :background-color colors/white}
[topbar/topbar {:title :t/privacy-and-security}]
[react/scroll-view {:padding-vertical 8}
@ -34,9 +38,7 @@
:accessory :switch
:on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched
((complement boolean) (= auth-method "biometric"))])}])
[react/view {:margin-vertical 8
:background-color colors/gray-lighter
:height 1}]
[separator]
;; TODO - uncomment when implemented
;; {:size :small
;; :title (i18n/label :t/change-password)
@ -61,9 +63,14 @@
:on-press #(re-frame/dispatch
[:multiaccounts.ui/preview-privacy-mode-switched
((complement boolean) preview-privacy?)])}]
(comment
{:container-margin-top 8
:size :small
:title :t/delete-my-account
:theme :negative})]]))
;; TODO(rasom): remove this condition when kk support will be added
(when-not keycard?
[separator])
(when-not keycard?
[quo/list-item
{:size :small
:theme :negative
:title (i18n/label :t/delete-my-profile)
:on-press #(re-frame/dispatch [:navigate-to :delete-profile])
:accessibility-label :dapps-permissions-button
:chevron true}])]]))

View File

@ -37,7 +37,8 @@
[status-im.ui.screens.keycard.settings.views :as keycard.settings]
[status-im.ui.components.tabbar.styles :as tabbar.styles]
[status-im.ui.screens.routing.core :as navigation]
[status-im.ui.screens.appearance.views :as appearance]))
[status-im.ui.screens.appearance.views :as appearance]
[status-im.ui.screens.privacy-and-security-settings.delete-profile :as delete-profile]))
(defonce stack (navigation/create-stack))
@ -115,6 +116,10 @@
:component profile.seed/backup-seed}
{:name :tribute-to-talk
:component tr-to-talk/tribute-to-talk}
{:name :delete-profile
:transition :presentation-ios
:insets {:bottom true}
:component delete-profile/delete-profile}
;; {:name:my-profile-ext-settings
;; :component}

View File

@ -2,7 +2,7 @@
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
"owner": "status-im",
"repo": "status-go",
"version": "v0.55.1",
"commit-sha1": "6d5a93287b2c1b4d4d5d1178a3ecec870bf18b9e",
"src-sha256": "1wly0km9bxxv1wwj6jchqh4d4x2m86fxrdqixjzldy70vl6qbyqa"
"version": "v0.55.2",
"commit-sha1": "0b3cdf7362bbdf9ba7fc11da803105f9417dfbac",
"src-sha256": "1vq3z150p0fbwjc1mqmi8lz4vg28dzqhlpsn7kar8j5z4rx5z5hn"
}

View File

@ -311,6 +311,11 @@
"delete-network-confirmation": "Are you sure you want to delete this network?",
"delete-network-error": "Please connect to a different network before deleting this one",
"delete-network-title": "Delete network?",
"delete-profile": "Delete profile",
"delete-my-profile": "Delete my profile",
"delete-profile-warning": "Warning: If you dont have your seed phrase written down, you will loose access to your funds after you delete your profile",
"profile-deleted-title": "Profile deleted",
"profile-deleted-content": "Your profile was successfully deleted",
"deny": "Deny",
"description": "Description",
"dev-mode": "Development mode",