Merge pull request #1865 from ecency/nt/tweaks

Nt/tweaks
This commit is contained in:
Feruz M 2021-03-20 16:34:02 +02:00 committed by GitHub
commit d49d6918f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 496 additions and 207 deletions

View File

@ -323,7 +323,10 @@
"enter_text": "Enter PIN to unlock",
"set_new": "Set new PIN",
"write_again": "Write again",
"forgot_text": "Oh, I forgot it..."
"forgot_text": "Oh, I forgot it...",
"pin_not_matched":"PIN do not match, Please try again.",
"attempts_postfix":"failed attempt(s)",
"message_reset_warning":"User data will be wiped on next failed attempt"
},
"alert": {
"success": "Success!",
@ -357,6 +360,7 @@
"remove_alert": "Are you sure you want to remove?",
"clear_alert": "Are you sure you want to clear?",
"clear_user_alert": "Are you sure you want to clear all user data?",
"decrypt_fail_alert":"Corrupt app state, please relogin to reset state.",
"clear": "Clear",
"cancel": "Cancel",
"delete": "Delete",

View File

@ -97,6 +97,7 @@ class InAppPurchaseContainer extends Component {
}),
);
} else if (get(error, 'responseCode') !== '2') {
console.warn('failed puchase:', error);
Alert.alert(
intl.formatMessage({
id: 'alert.warning',

View File

@ -29,7 +29,11 @@ export const getCurrencyTokenRate = (currency, token) =>
/**
* @params username
*/
export const getDrafts = (username) => api.get(`/drafts/${username}`).then((resp) => resp.data);
export const getDrafts = (username) =>
api
.get(`/drafts/${username}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/*export const getDrafts = data =>
new Promise((resolve, reject) => {
@ -114,7 +118,8 @@ export const addBookmark = (username, author, permlink) =>
permlink,
chain: 'hive',
})
.then((resp) => resp.data);
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
export const addReport = (url) =>
api
@ -127,7 +132,10 @@ export const addReport = (url) =>
* @params current username
*/
export const getBookmarks = (username) =>
api.get(`/bookmarks/${username}`).then((resp) => resp.data);
api
.get(`/bookmarks/${username}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/**
* @params id
@ -139,14 +147,20 @@ export const removeBookmark = (username, id) => api.delete(`/bookmarks/${usernam
* @params current username
*/
export const getFavorites = (username) =>
api.get(`/favorites/${username}`).then((resp) => resp.data);
api
.get(`/favorites/${username}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/**
* @params current username
* @params target username
*/
export const getIsFavorite = (targetUsername, currentUsername) =>
api.get(`/isfavorite/${currentUsername}/${targetUsername}`).then((resp) => resp.data);
api
.get(`/isfavorite/${currentUsername}/${targetUsername}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/**
* @params current username
@ -158,7 +172,8 @@ export const addFavorite = (currentUsername, targetUsername) =>
username: currentUsername,
account: targetUsername,
})
.then((resp) => resp.data);
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/**
* @params current username
@ -171,7 +186,10 @@ export const removeFavorite = (currentUsername, targetUsername) =>
* @params current username
*/
export const getSnippets = (username) =>
api.get(`/fragments/${username}`).then((resp) => resp.data);
api
.get(`/fragments/${username}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/**
* @params current username
@ -185,7 +203,8 @@ export const addSnippet = (currentUsername, title, body) =>
title,
body,
})
.then((resp) => resp.data);
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
/**
* @params current username
@ -413,10 +432,14 @@ export const schedule = (
options,
reblog: 0,
})
.then((resp) => resp.data);
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
export const getSchedules = (username) =>
api.get(`/schedules/${username}`).then((resp) => resp.data);
api
.get(`/schedules/${username}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
export const removeSchedule = (username, id) => api.delete(`/schedules/${username}/${id}`);
@ -425,7 +448,11 @@ export const moveSchedule = (id, username) => api.put(`/schedules/${username}/${
// Old image service
// Images
export const getImages = (username) => api.get(`api/images/${username}`).then((resp) => resp.data);
export const getImages = (username) =>
api
.get(`api/images/${username}`)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
export const addMyImage = (user, url) =>
api.post('/image', {
@ -467,11 +494,14 @@ export const getNodes = () => serverList.get().then((resp) => resp.data.hived ||
export const getSCAccessToken = (code) =>
new Promise((resolve, reject) => {
ecencyApi
.post('/hs-token-refresh', {
.post('/auth-api/hs-token-refresh', {
code,
})
.then((resp) => resolve(resp.data))
.catch((e) => reject(e));
.catch((e) => {
bugsnag.notify(e);
reject(e);
});
});
export const getPromotePosts = () => {
@ -479,11 +509,16 @@ export const getPromotePosts = () => {
console.log('Fetching promoted posts');
return api.get('/promoted-posts?limit=10').then((resp) => resp.data);
} catch (error) {
bugsnag.notify(error);
return error;
}
};
export const purchaseOrder = (data) => api.post('/purchase-order', data).then((resp) => resp.data);
export const purchaseOrder = (data) =>
api
.post('/purchase-order', data)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));
export const getPostReblogs = (data) =>
api
@ -492,4 +527,7 @@ export const getPostReblogs = (data) =>
.catch((error) => bugsnag.notify(error));
export const register = (data) =>
api.post('/signup/account-create', data).then((resp) => resp.data);
api
.post('/signup/account-create', data)
.then((resp) => resp.data)
.catch((error) => bugsnag.notify(error));

View File

@ -203,6 +203,7 @@ export const setUserDataWithPinCode = async (data) => {
return updatedUserData;
} catch (error) {
console.warn('Failed to set user data with pin: ', data, error);
return Promise.reject(new Error('auth.unknow_error'));
}
};
@ -212,72 +213,98 @@ export const updatePinCode = (data) =>
let currentUser = null;
try {
setPinCode(get(data, 'pinCode'));
getUserData().then(async (users) => {
if (users && users.length > 0) {
await users.forEach((userData) => {
if (
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.ACTIVE_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.MEMO_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.POSTING_KEY
) {
const publicKey =
get(userData, 'masterKey') ||
get(userData, 'activeKey') ||
get(userData, 'memoKey') ||
get(userData, 'postingKey');
getUserData()
.then(async (users) => {
const _onDecryptError = () => {
throw new Error('Decryption failed');
};
if (users && users.length > 0) {
users.forEach((userData) => {
if (
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.ACTIVE_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.MEMO_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.POSTING_KEY
) {
const publicKey =
get(userData, 'masterKey') ||
get(userData, 'activeKey') ||
get(userData, 'memoKey') ||
get(userData, 'postingKey');
data.password = decryptKey(publicKey, get(data, 'oldPinCode', ''));
} else if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
data.accessToken = decryptKey(
get(userData, 'accessToken'),
get(data, 'oldPinCode', ''),
);
}
const updatedUserData = getUpdatedUserData(userData, data);
updateUserData(updatedUserData);
if (userData.username === data.username) {
currentUser = updatedUserData;
}
});
resolve(currentUser);
}
});
const password = decryptKey(
publicKey,
get(data, 'oldPinCode', ''),
_onDecryptError,
);
if (password === undefined) {
return;
}
data.password = password;
} else if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
const accessToken = decryptKey(
get(userData, 'accessToken'),
get(data, 'oldPinCode', ''),
_onDecryptError,
);
if (accessToken === undefined) {
return;
}
data.accessToken = accessToken;
}
const updatedUserData = getUpdatedUserData(userData, data);
updateUserData(updatedUserData);
if (userData.username === data.username) {
currentUser = updatedUserData;
}
});
resolve(currentUser);
}
})
.catch((err) => {
reject(err);
});
} catch (error) {
reject(error.message);
}
});
export const verifyPinCode = async (data) => {
const pinHash = await getPinCode();
try {
const pinHash = await getPinCode();
const result = await getUserDataWithUsername(data.username);
const userData = result[0];
const result = await getUserDataWithUsername(data.username);
const userData = result[0];
// This is migration for new pin structure, it will remove v2.2
if (!pinHash) {
try {
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
decryptKey(get(userData, 'accessToken'), get(data, 'pinCode'));
} else {
decryptKey(userData.masterKey, get(data, 'pinCode'));
// This is migration for new pin structure, it will remove v2.2
if (!pinHash) {
try {
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
decryptKey(get(userData, 'accessToken'), get(data, 'pinCode'));
} else {
decryptKey(userData.masterKey, get(data, 'pinCode'));
}
await setPinCode(get(data, 'pinCode'));
} catch (error) {
return Promise.reject(new Error('Invalid pin code, please check and try again'));
}
await setPinCode(get(data, 'pinCode'));
} catch (error) {
return Promise.reject(new Error('Invalid pin code, please check and try again'));
}
}
if (sha256(get(data, 'pinCode')).toString() !== pinHash) {
return Promise.reject(new Error('auth.invalid_pin'));
}
if (result.length > 0) {
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
await refreshSCToken(userData, get(data, 'pinCode'));
if (sha256(get(data, 'pinCode')).toString() !== pinHash) {
return Promise.reject(new Error('auth.invalid_pin'));
}
if (result.length > 0) {
if (get(userData, 'authType', '') === AUTH_TYPE.STEEM_CONNECT) {
await refreshSCToken(userData, get(data, 'pinCode'));
}
}
return true;
} catch (err) {
console.warn('Failed to verify pin in auth: ', data, err);
return Promise.reject(err);
}
return true;
};
export const refreshSCToken = async (userData, pinCode) => {

View File

@ -47,6 +47,7 @@ export const getUserDataWithUsername = async (username) => {
}
return [];
} catch (error) {
console.warn('Failed to get user data: ', error);
return error;
}
};
@ -236,6 +237,7 @@ export const getPinCode = async () => {
}
return '';
} catch (error) {
console.warn('Failed get auth from storage: ', error);
return error;
}
};
@ -508,6 +510,7 @@ export const getSettings = async () => {
mentionNotification: true,
reblogNotification: true,
transfersNotification: true,
isPinCodeOpen: false,
};
await setItemToStorage(SETTINGS_SCHEMA, settingData);
return settingData;

View File

@ -45,6 +45,7 @@ const Application = () => {
isFullScreen
swipeToClose={false}
backButtonClose={false}
style={{ margin: 0 }}
>
<PinCode />
</Modal>
@ -53,6 +54,7 @@ const Application = () => {
isFullScreen
swipeToClose={false}
backButtonClose={false}
style={{ margin: 0 }}
>
<WelcomeScreen handleButtonPress={handleWelcomeModalButtonPress} />
</Modal>

View File

@ -4,6 +4,7 @@ import scalePx from '../../../utils/scalePx';
export default EStyleSheet.create({
safeAreaView: {
flex: 1,
backgroundColor: '$primaryBackgroundColor',
},
container: {
flex: 0.95,

View File

@ -10,6 +10,7 @@ import { navigate } from '../../../navigation/service';
import { setUserDataWithPinCode, verifyPinCode, updatePinCode } from '../../../providers/hive/auth';
import {
closePinCodeModal,
isPinCodeOpen,
login,
logoutDone,
setPinCode as savePinCode,
@ -21,6 +22,7 @@ import {
removeAllUserData,
removePinCode,
setAuthStatus,
setPinCodeOpen,
} from '../../../realm/realm';
import { updateCurrentAccount, removeOtherAccount } from '../../../redux/actions/accountAction';
import { getUser } from '../../../providers/hive/dhive';
@ -37,9 +39,10 @@ class PinCodeContainer extends Component {
this.state = {
isExistUser: null,
informationText: '',
pinCode: null,
newPinCode: null,
isOldPinVerified: get(props.pinCodeParams, 'isOldPinVerified', false),
oldPinCode: get(props.pinCodeParams, 'oldPinCode', null),
failedAttempts: 0,
};
}
@ -48,9 +51,9 @@ class PinCodeContainer extends Component {
componentDidMount() {
this._getDataFromStorage().then(() => {
const { intl } = this.props;
const { isExistUser } = this.state;
const { isOldPinVerified } = this.state;
if (isExistUser) {
if (!isOldPinVerified) {
this.setState({
informationText: intl.formatMessage({
id: 'pincode.enter_text',
@ -78,6 +81,26 @@ class PinCodeContainer extends Component {
});
});
//this function updates realm with appropriate master key required for encyrption
//this function is important: must run while chaning pin
//and even logging in with existing pin code
_updatePinCodeRealm = async (pinData) => {
try {
const { currentAccount, dispatch } = this.props;
const response = await updatePinCode(pinData);
if (!response) {
return false;
}
const _currentAccount = currentAccount;
_currentAccount.local = response;
dispatch(updateCurrentAccount({ ..._currentAccount }));
return true;
} catch (err) {
this._onDecryptFail();
return false;
}
};
_resetPinCode = (pin) =>
new Promise((resolve, reject) => {
const {
@ -86,7 +109,7 @@ class PinCodeContainer extends Component {
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
intl,
} = this.props;
const { isOldPinVerified, oldPinCode } = this.state;
const { isOldPinVerified, oldPinCode, newPinCode } = this.state;
const pinData = {
pinCode: pin,
@ -96,27 +119,58 @@ class PinCodeContainer extends Component {
oldPinCode,
};
//if old pin already verified, check new pin setup conditions.
if (isOldPinVerified) {
updatePinCode(pinData).then((response) => {
const _currentAccount = currentAccount;
_currentAccount.local = response;
//if newPin already exist and pin is a valid pin, compare and set new pin
if (pin !== undefined && pin === newPinCode) {
this._updatePinCodeRealm(pinData).then((status) => {
if (!status) {
resolve();
return;
}
this._savePinCode(pin);
if (callback) {
callback(pin, oldPinCode);
}
dispatch(closePinCodeModal());
if (navigateTo) {
navigate({
routeName: navigateTo,
params: navigateParams,
});
}
resolve();
});
}
dispatch(updateCurrentAccount({ ..._currentAccount }));
this._savePinCode(pin);
if (callback) {
callback(pin, oldPinCode);
}
dispatch(closePinCodeModal());
if (navigateTo) {
navigate({
routeName: navigateTo,
params: navigateParams,
});
}
// if newPin code exists and above case failed, that means pins did not match
// warn user about it and do nothing
else if (newPinCode) {
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
}),
intl.formatMessage({
id: 'pincode.pin_not_matched',
}),
);
resolve();
});
} else {
}
//if newPinCode do yet exist, save it in state and prompt user to reneter pin.
else {
this.setState({
informationText: intl.formatMessage({
id: 'pincode.write_again',
}),
newPinCode: pin,
});
resolve();
}
}
// if old pin code is not yet verified attempt to verify code
else {
verifyPinCode(pinData)
.then(() => {
this.setState({ isOldPinVerified: true });
@ -124,12 +178,13 @@ class PinCodeContainer extends Component {
informationText: intl.formatMessage({
id: 'pincode.set_new',
}),
pinCode: null,
newPinCode: null,
oldPinCode: pin,
});
resolve();
})
.catch((err) => {
console.warn('Failed to verify pin code', err);
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
@ -183,13 +238,12 @@ class PinCodeContainer extends Component {
});
});
_verifyPinCode = (pin) =>
_verifyPinCode = (pin, { shouldUpdateRealm } = {}) =>
new Promise((resolve, reject) => {
const {
currentAccount,
dispatch,
pinCodeParams: { navigateTo, navigateParams, accessToken, callback },
intl,
} = this.props;
const { oldPinCode } = this.state;
@ -204,11 +258,20 @@ class PinCodeContainer extends Component {
.then(() => {
this._savePinCode(pin);
getUserDataWithUsername(currentAccount.name).then((realmData) => {
const _currentAccount = currentAccount;
_currentAccount.username = _currentAccount.name;
[_currentAccount.local] = realmData;
dispatch(updateCurrentAccount({ ..._currentAccount }));
dispatch(closePinCodeModal());
if (shouldUpdateRealm) {
this._updatePinCodeRealm(pinData).then(() => {
dispatch(closePinCodeModal());
});
} else {
const _currentAccount = currentAccount;
_currentAccount.username = _currentAccount.name;
[_currentAccount.local] = realmData;
dispatch(updateCurrentAccount({ ..._currentAccount }));
dispatch(closePinCodeModal());
}
//on successful code verification run requested operation passed as props
if (callback) {
callback(pin, oldPinCode);
}
@ -218,17 +281,11 @@ class PinCodeContainer extends Component {
params: navigateParams,
});
}
resolve();
});
})
.catch((err) => {
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
}),
intl.formatMessage({
id: err.message,
}),
);
console.warn('code verification for login failed: ', err);
reject(err);
});
});
@ -239,22 +296,104 @@ class PinCodeContainer extends Component {
dispatch(savePinCode(encryptedPin));
};
_forgotPinCode = async () => {
const { otherAccounts, dispatch } = this.props;
await removeAllUserData()
.then(async () => {
dispatch(updateCurrentAccount({}));
dispatch(login(false));
removePinCode();
setAuthStatus({ isLoggedIn: false });
setExistUser(false);
if (otherAccounts.length > 0) {
otherAccounts.map((item) => dispatch(removeOtherAccount(item.username)));
}
dispatch(logoutDone());
dispatch(closePinCodeModal());
dispatch(isPinCodeOpen(false));
setPinCodeOpen(false);
})
.catch((err) => {
console.warn('Failed to remove user data', err);
});
};
_handleFailedAttempt = (error) => {
console.warn('Failed to set pin: ', error);
const { intl } = this.props;
const { failedAttempts } = this.state;
//increment failed attempt
const totalAttempts = failedAttempts + 1;
if (totalAttempts < 3) {
//shwo failure alert box
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
}),
intl.formatMessage({
id: error.message,
}),
);
let infoMessage = intl.formatMessage({
id: 'pincode.enter_text',
});
infoMessage += `, ${totalAttempts} ${intl.formatMessage({ id: 'pincode.attempts_postfix' })}`;
if (totalAttempts > 1) {
infoMessage += `\n${intl.formatMessage({ id: 'pincode.message_reset_warning' })}`;
}
this.setState({
failedAttempts: totalAttempts,
informationText: infoMessage,
});
return false;
} else {
//wipe user data
this._forgotPinCode();
return true;
}
};
_onDecryptFail = () => {
const { intl } = this.props;
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
}),
intl.formatMessage({
id: 'alert.decrypt_fail_alert',
}),
[
{ text: intl.formatMessage({ id: 'alert.clear' }), onPress: () => this._forgotPinCode() },
{ text: intl.formatMessage({ id: 'alert.cancel' }), style: 'destructive' },
],
);
};
_setPinCode = async (pin, isReset) => {
const { intl, currentAccount, applicationPinCode } = this.props;
const { isExistUser, pinCode } = this.state;
const { isExistUser } = this.state;
try {
const realmData = await getUserDataWithUsername(currentAccount.name);
const userData = realmData[0];
// For exist users
// check if reset routine is triggered by user, reroute code to reset hanlder
if (isReset) {
await this._resetPinCode(pin);
return true;
}
//user is logged in and is not reset routine...
if (isExistUser) {
if (!userData.accessToken && !userData.masterKey && applicationPinCode) {
const verifiedPin = decryptKey(applicationPinCode, Config.PIN_KEY);
const verifiedPin = decryptKey(applicationPinCode, Config.PIN_KEY, this._onDecryptFail);
if (verifiedPin === undefined) {
return true;
}
if (verifiedPin === pin) {
await this._setFirstPinCode(pin);
} else {
@ -273,47 +412,16 @@ class PinCodeContainer extends Component {
return true;
}
// For new users
if (pinCode === pin) {
await this._setFirstPinCode(pin);
//means this is not reset routine and user do not exist
//only possible option left is user logging int,
//verifyPinCode then and update realm as well.
else {
await this._verifyPinCode(pin, { shouldUpdateRealm: true });
return true;
}
} catch (error) {
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
}),
intl.formatMessage({
id: error.message,
}),
);
return this._handleFailedAttempt(error);
}
if (!pinCode) {
// If the user is logging in for the first time, the user should set to pin
await this.setState({
informationText: intl.formatMessage({
id: 'pincode.write_again',
}),
pinCode: pin,
});
return Promise.resolve();
}
await this.setState({
informationText: intl.formatMessage({
id: 'pincode.write_again',
}),
});
await setTimeout(() => {
this.setState({
informationText: intl.formatMessage({
id: 'pincode.set_new',
}),
pinCode: null,
});
return Promise.resolve();
}, 1000);
};
_handleForgotButton = () => {
@ -333,38 +441,19 @@ class PinCodeContainer extends Component {
);
};
_forgotPinCode = async () => {
const { otherAccounts, dispatch } = this.props;
await removeAllUserData()
.then(async () => {
dispatch(updateCurrentAccount({}));
dispatch(login(false));
removePinCode();
setAuthStatus({ isLoggedIn: false });
setExistUser(false);
if (otherAccounts.length > 0) {
otherAccounts.map((item) => dispatch(removeOtherAccount(item.username)));
}
dispatch(logoutDone());
dispatch(closePinCodeModal());
})
.catch(() => {});
};
render() {
const {
currentAccount,
intl,
pinCodeParams: { isReset },
} = this.props;
const { informationText, isExistUser } = this.state;
const { informationText, isOldPinVerified, isExistUser } = this.state;
return (
<PinCodeScreen
informationText={informationText}
setPinCode={(pin) => this._setPinCode(pin, isReset)}
showForgotButton={isExistUser}
showForgotButton={!isOldPinVerified}
username={currentAccount.name}
intl={intl}
handleForgotButton={() => this._handleForgotButton()}

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { useIntl } from 'react-intl';
import { Text, TouchableOpacity, View } from 'react-native';
@ -18,28 +18,37 @@ const PinCodeScreen = ({
const [loading, setLoading] = useState(false);
const intl = useIntl();
const _handleKeyboardOnPress = async (value) => {
if (loading) {
return;
}
if (value === 'clear') {
setPin('');
return;
}
const newPin = `${pin}${value}`;
if (pin.length < 3) {
setPin(newPin);
} else if (pin.length === 3) {
await setPin(newPin);
await setLoading(true);
await setPinCode(`${pin}${value}`);
useEffect(() => {
_handlePinComplete();
}, [pin]);
const _handlePinComplete = async () => {
if (pin.length === 4) {
setLoading(true);
await setPinCode(pin);
setPin('');
setLoading(false);
} else if (pin.length > 3) {
setPin(`${value}`);
}
};
const _handleKeyboardOnPress = async (value) => {
try {
if (loading) {
return;
}
if (value === 'clear') {
setPin('');
return;
}
const newPin = `${pin}${value}`;
if (pin.length < 4) {
setPin(newPin);
} else if (pin.length >= 4) {
setPin(`${value}`);
}
} catch (err) {
console.warn('Failed to handle keyboard press as expected', err);
}
};

View File

@ -26,6 +26,7 @@ export default EStyleSheet.create({
},
informationText: {
color: '$editorButtonColor',
textAlign: 'center',
},
informationView: {
flex: 1,

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Platform } from 'react-native';
import { Platform, Alert } from 'react-native';
import { connect } from 'react-redux';
import { Client } from '@hiveio/dhive';
import VersionNumber from 'react-native-version-number';
@ -17,6 +17,11 @@ import {
setNsfw as setNsfw2DB,
setTheme,
setPinCodeOpen,
removeUserData,
removePinCode,
setAuthStatus,
setExistUser,
removeAllUserData,
} from '../../../realm/realm';
// Services and Actions
@ -31,12 +36,15 @@ import {
setNsfw,
isPinCodeOpen,
setPinCode as savePinCode,
login,
logoutDone,
closePinCodeModal,
} from '../../../redux/actions/applicationActions';
import { toastNotification } from '../../../redux/actions/uiAction';
import { setPushToken, getNodes } from '../../../providers/ecency/ecency';
import { checkClient } from '../../../providers/hive/dhive';
import { updatePinCode } from '../../../providers/hive/auth';
import { updateCurrentAccount } from '../../../redux/actions/accountAction';
import { removeOtherAccount, updateCurrentAccount } from '../../../redux/actions/accountAction';
// Middleware
// Constants
@ -361,32 +369,82 @@ class SettingsContainer extends Component {
}
};
_clearUserData = async () => {
const { otherAccounts, dispatch } = this.props;
await removeAllUserData()
.then(async () => {
dispatch(updateCurrentAccount({}));
dispatch(login(false));
removePinCode();
setAuthStatus({ isLoggedIn: false });
setExistUser(false);
if (otherAccounts.length > 0) {
otherAccounts.map((item) => dispatch(removeOtherAccount(item.username)));
}
dispatch(logoutDone());
dispatch(isPinCodeOpen(false));
setPinCodeOpen(false);
})
.catch((err) => {
console.warn('Failed to remove user data', err);
});
};
_onDecryptFail = () => {
const { intl } = this.props;
setTimeout(() => {
Alert.alert(
intl.formatMessage({
id: 'alert.warning',
}),
intl.formatMessage({
id: 'alert.decrypt_fail_alert',
}),
[
{ text: intl.formatMessage({ id: 'alert.clear' }), onPress: () => this._clearUserData() },
{ text: intl.formatMessage({ id: 'alert.cancel' }), style: 'destructive' },
],
);
}, 500);
};
_setDefaultPinCode = (action) => {
const { dispatch, username, currentAccount, pinCode } = this.props;
if (!action) {
const oldPinCode = decryptKey(pinCode, Config.PIN_KEY);
const oldPinCode = decryptKey(pinCode, Config.PIN_KEY, this._onDecryptFail);
if (oldPinCode === undefined) {
return;
}
const pinData = {
pinCode: Config.DEFAULT_PIN,
username,
oldPinCode,
};
updatePinCode(pinData).then((response) => {
const _currentAccount = currentAccount;
_currentAccount.local = response;
updatePinCode(pinData)
.then((response) => {
const _currentAccount = currentAccount;
_currentAccount.local = response;
dispatch(
updateCurrentAccount({
..._currentAccount,
}),
);
dispatch(
updateCurrentAccount({
..._currentAccount,
}),
);
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
dispatch(savePinCode(encryptedPin));
const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
dispatch(savePinCode(encryptedPin));
setPinCodeOpen(action);
dispatch(isPinCodeOpen(action));
});
setPinCodeOpen(action);
dispatch(isPinCodeOpen(action));
})
.catch((err) => {
console.warn('pin update failure: ', err);
this._onDecryptFail();
});
} else {
setPinCodeOpen(action);
dispatch(isPinCodeOpen(action));
@ -427,7 +485,6 @@ const mapStateToProps = (state) => ({
selectedApi: state.application.api,
selectedCurrency: state.application.currency,
selectedLanguage: state.application.language,
username: state.account.currentAccount && state.account.currentAccount.name,
currentAccount: state.account.currentAccount,
otherAccounts: state.account.otherAccounts,

View File

@ -1,7 +1,64 @@
import CryptoJS from 'crypto-js';
export const encryptKey = (key, data) => CryptoJS.AES.encrypt(key, data).toString();
const STAMP = '995a06d5-ee54-407f-bb8e-e4af2ab2fe01';
export const decryptKey = (key, data) => {
return CryptoJS.AES.decrypt(key, data).toString(CryptoJS.enc.Utf8);
export const encryptKey = (data, key) => {
console.log('encrypting: ', data, key);
const stampedData = getStampedData(data);
const encJson = CryptoJS.AES.encrypt(JSON.stringify(stampedData), key).toString();
let encData = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encJson));
console.log('returning: ', encData);
return encData;
};
export const decryptKey = (data, key, onError) => {
const legacyDecrypt = () => decryptKeyLegacy(data, key, onError);
try {
const response = decryptKeyNew(data, key);
return response;
} catch (err) {
console.warn('decryption with new method failed, trying legacy', err);
return legacyDecrypt();
}
};
const decryptKeyNew = (data, key) => {
console.log('decrypting new: ', data, key);
let decData = CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8);
let bytes = CryptoJS.AES.decrypt(decData, key).toString(CryptoJS.enc.Utf8);
const stampedData = JSON.parse(bytes);
const ret = processStampedData(stampedData);
console.log('returning: ', ret);
return ret;
};
const decryptKeyLegacy = (data, key, onError) => {
try {
console.log('decrypting legacy ', data, key);
const ret = CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8);
console.log('returning: ', ret);
return ret;
} catch (err) {
console.warn('decryption with legacy failed as well');
if (onError) {
onError(err);
}
}
};
// stamping mechanism will help distinguish old legacy data and new encrypted data
// second purpose is to avoid necrypting empty strings
const getStampedData = (data) => {
return {
data,
stamp: STAMP,
};
};
const processStampedData = (stampedData) => {
if (stampedData.hasOwnProperty('stamp') && stampedData.stamp == STAMP) {
return stampedData.data;
}
throw new Error('Possibly un-stamped legacy data');
};