derive 3 keys, consolidate sync redux
This commit is contained in:
parent
ded2992e44
commit
6b21df2c17
12 changed files with 271 additions and 446 deletions
|
@ -22,20 +22,21 @@ export function checkHmac(serverWalletState, hmacKey, hmac) {
|
|||
}
|
||||
|
||||
export function deriveSecrets(rootPassword, email, saltSeed, callback) {
|
||||
const encodedPassword = Buffer.from(rootPassword.normalize('NFKC'))
|
||||
const encodedEmail = Buffer.from(email)
|
||||
const encodedPassword = Buffer.from(rootPassword.normalize('NFKC'));
|
||||
const encodedEmail = Buffer.from(email);
|
||||
const SCRYPT_N = 1 << 20;
|
||||
const SCRYPT_R = 8;
|
||||
const SCRYPT_P = 1;
|
||||
const KEY_LENGTH = 32;
|
||||
const NUM_KEYS = 2;
|
||||
const NUM_KEYS = 3;
|
||||
const MAXMEM_MULTIPLIER = 256;
|
||||
const DEFAULT_MAXMEM = MAXMEM_MULTIPLIER * SCRYPT_N * SCRYPT_R;
|
||||
|
||||
function getKeyParts(key) {
|
||||
const lbryIdPassword = key.slice(0, KEY_LENGTH).toString('base64');
|
||||
const hmacKey = key.slice(KEY_LENGTH).toString('base64');
|
||||
return { lbryIdPassword, hmacKey }; // Buffer aa bb cc 6c
|
||||
const hmacKey = key.slice(KEY_LENGTH, KEY_LENGTH * 2).toString('base64');
|
||||
const dataKey = key.slice(KEY_LENGTH * 2).toString('base64');
|
||||
return { lbryIdPassword, hmacKey, dataKey }; // Buffer aa bb cc 6c
|
||||
}
|
||||
|
||||
const salt = generateSalt(encodedEmail, saltSeed);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import test from 'tape';
|
||||
// import sync from '../sync.js';
|
||||
import { generateSalt, generateSaltSeed, deriveSecrets, walletHmac } from './sync.js';
|
||||
export default function doTest () {
|
||||
export default function doTest() {
|
||||
|
||||
test('Generate sync seed', (assert) => {
|
||||
const seed = generateSaltSeed();
|
||||
|
@ -29,12 +29,11 @@ export default function doTest () {
|
|||
|
||||
const expectedHmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8='; //base64
|
||||
const expectedLbryIdPassword = 'HKo/J+x4Hsy2NkMvj2JB9RI0yrvEiB4QSA/NHPaT/cA=';
|
||||
let result;
|
||||
|
||||
function cb(e, r) {
|
||||
console.log('result', r)
|
||||
assert.equal(r.keys.hmacKey, expectedHmacKey, 'hmac is expected value');
|
||||
assert.equal(r.keys.lbryIdPassword, expectedLbryIdPassword, 'lbryid password is expected value');
|
||||
console.log('result', r);
|
||||
assert.equal(r.hmacKey, expectedHmacKey, 'hmac is expected value');
|
||||
assert.equal(r.lbryIdPassword, expectedLbryIdPassword, 'lbryid password is expected value');
|
||||
assert.end();
|
||||
}
|
||||
|
||||
|
@ -51,8 +50,7 @@ export default function doTest () {
|
|||
const hmacHex = walletHmac(input_str);
|
||||
assert.equal(hmacHex, expectedHmacHex);
|
||||
assert.end();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
doTest()
|
||||
doTest();
|
||||
|
|
|
@ -6,7 +6,7 @@ import { selectGetSyncErrorMessage } from 'redux/selectors/sync';
|
|||
import { selectHasNavigated } from 'redux/selectors/app';
|
||||
import { selectTotalBalance, selectBalance } from 'redux/selectors/wallet';
|
||||
import * as SETTINGS from 'constants/settings';
|
||||
import { doLbrysyncRegister } from 'redux/actions/lbrysync';
|
||||
import { doLbrysyncRegister } from 'redux/actions/sync';
|
||||
import Header from './view';
|
||||
|
||||
const select = (state) => ({
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
selectLbrySyncEncryptedHmacKey,
|
||||
selectLbrySyncEncryptedRoot,
|
||||
selectLbrySyncEncryptedProviderPass,
|
||||
} from 'redux/selectors/lbrysync';
|
||||
} from 'redux/selectors/sync';
|
||||
|
||||
import {
|
||||
doLbrysyncGetSalt,
|
||||
|
@ -25,10 +25,10 @@ import {
|
|||
doGenerateSaltSeed,
|
||||
doDeriveSecrets,
|
||||
doLbrysyncAuthenticate,
|
||||
} from 'redux/actions/lbrysync';
|
||||
} from 'redux/actions/sync';
|
||||
|
||||
const select = (state) => ({
|
||||
walletEncrypted: selectWalletIsEncrypted(state),
|
||||
isWalletEncrypted: selectWalletIsEncrypted(state),
|
||||
registering: selectLbrySyncRegistering(state),
|
||||
registeredEmail: selectLbrySyncEmail(state),
|
||||
registerError: selectLbrySyncRegisterError(state),
|
||||
|
|
|
@ -5,7 +5,6 @@ import Card from 'component/common/card';
|
|||
import Button from 'component/button';
|
||||
import { Form, FormField } from 'component/common/form';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import Spinner from 'component/spinner';
|
||||
import * as ICONS from 'constants/icons';
|
||||
|
||||
type Props = {
|
||||
|
@ -55,6 +54,9 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
register,
|
||||
} = props;
|
||||
|
||||
/*
|
||||
Register / auth
|
||||
*/
|
||||
const SIGN_IN_MODE = 'sign_in';
|
||||
const SIGN_UP_MODE = 'sign_up';
|
||||
const VERIFY_MODE = 'verify';
|
||||
|
@ -192,7 +194,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
/>
|
||||
</>
|
||||
}
|
||||
label={__('Password Again')}
|
||||
label={__('Password')}
|
||||
value={pass}
|
||||
onChange={(e) => setPass(e.target.value)}
|
||||
/>
|
||||
|
@ -222,7 +224,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
<p>
|
||||
<I18nMessage
|
||||
tokens={{
|
||||
sign_in: <Button button="link" onClick={() => setMode(SIGN_UP_MODE)} label={__('Sign in')} />,
|
||||
sign_in: <Button button="link" onClick={() => setMode(SIGN_IN_MODE)} label={__('Sign in')} />,
|
||||
}}
|
||||
>
|
||||
Sign up for a sync account. Or %sign_in%.
|
||||
|
@ -254,7 +256,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
/>
|
||||
</>
|
||||
}
|
||||
label={__('Password Again')}
|
||||
label={__('Password')}
|
||||
value={pass}
|
||||
onChange={(e) => setPass(e.target.value)}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import { connectRouter } from 'connected-react-router';
|
||||
import { costInfoReducer, blacklistReducer, filteredReducer, statsReducer } from 'lbryinc';
|
||||
import { lbrysyncReducer } from 'redux/reducers/lbrysync';
|
||||
import { claimsReducer } from 'redux/reducers/claims';
|
||||
import { fileInfoReducer } from 'redux/reducers/file_info';
|
||||
import { walletReducer } from 'redux/reducers/wallet';
|
||||
|
@ -44,5 +43,4 @@ export default (history) =>
|
|||
wallet: walletReducer,
|
||||
sync: syncReducer,
|
||||
collections: collectionsReducer,
|
||||
lbrysync: lbrysyncReducer,
|
||||
});
|
||||
|
|
|
@ -1,302 +0,0 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { safeStoreEncrypt, safeStoreDecrypt } from 'util/saved-passwords';
|
||||
|
||||
import * as Lbrysync from 'lbrysync';
|
||||
import Lbry from 'lbry';
|
||||
import { Lbryio } from "lbryinc";
|
||||
import { selectSyncHash } from '../selectors/sync';
|
||||
export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
||||
const { fetchSaltSeed } = Lbrysync;
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
||||
});
|
||||
try {
|
||||
const saltOrError = await fetchSaltSeed(email);
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
||||
data: { email: email, saltSeed: saltOrError},
|
||||
});
|
||||
return saltOrError;
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
||||
data: { email: email, saltError: 'Not Found'},
|
||||
});
|
||||
return 'not found';
|
||||
}
|
||||
};
|
||||
|
||||
// register an email (eventually username)
|
||||
export const doLbrysyncRegister = (email: string, secrets: any, saltSeed: string) => async (dispatch: Dispatch) => {
|
||||
const { register } = Lbrysync;
|
||||
// started
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
||||
});
|
||||
const resultIfError = await register(email, secrets.providerPass, saltSeed);
|
||||
const encProviderPass = safeStoreEncrypt(secrets.providerPass);
|
||||
const encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
||||
const enctyptedRoot = safeStoreEncrypt(secrets.rootPassword);
|
||||
const registerData = {
|
||||
email,
|
||||
saltSeed,
|
||||
providerPass: encProviderPass,
|
||||
hmacKey: encHmacKey,
|
||||
rootPass: enctyptedRoot,
|
||||
};
|
||||
|
||||
if (!resultIfError) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
||||
data: registerData,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
||||
data: resultIfError,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// get token given username/password
|
||||
export const doLbrysyncAuthenticate =
|
||||
() => async (dispatch: Dispatch, getState: GetState) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_STARTED,
|
||||
});
|
||||
const state = getState();
|
||||
const { lbrysync } = state;
|
||||
const { registeredEmail: email, encryptedProviderPass } = lbrysync;
|
||||
const status = await Lbry.status();
|
||||
const { installation_id: deviceId } = status;
|
||||
const password = safeStoreDecrypt(encryptedProviderPass);
|
||||
|
||||
const { getAuthToken } = Lbrysync;
|
||||
|
||||
const result: { token?: string, error?: string } = await getAuthToken(email, password, deviceId);
|
||||
|
||||
if (result.token) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
||||
data: result.token,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_FAILED,
|
||||
data: result.error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const doGenerateSaltSeed = () => async (dispatch: Dispatch) => {
|
||||
const result = await ipcRenderer.invoke('invoke-get-salt-seed');
|
||||
return result;
|
||||
};
|
||||
|
||||
export const doDeriveSecrets = (rootPassword: string, email: string, saltSeed: string) => async (dispatch: Dispatch) =>
|
||||
{
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_STARTED,
|
||||
});
|
||||
try {
|
||||
const result = await ipcRenderer.invoke('invoke-get-secrets', rootPassword, email, saltSeed);
|
||||
|
||||
const data = {
|
||||
hmacKey: result.hmacKey,
|
||||
rootPassword,
|
||||
providerPass: result.lbryIdPassword,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_COMPLETED,
|
||||
data,
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_FAILED,
|
||||
data: {
|
||||
error: e,
|
||||
},
|
||||
});
|
||||
return { error: e.message };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export async function doSetSync() {
|
||||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const localHash = selectSyncHash(state);
|
||||
const { lbrysync } = state;
|
||||
const { authToken, encryptedRoot } = lbrysync;
|
||||
dispatch({
|
||||
type: ACTIONS.SET_SYNC_STARTED,
|
||||
});
|
||||
let error;
|
||||
|
||||
try {
|
||||
const status = Lbry.wallet_status();
|
||||
if (status.is_locked) {
|
||||
throw new Error('Error parsing i18n messages file: ' + messagesFilePath + ' err: ' + err);
|
||||
}
|
||||
} catch(e) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
const syncData = await Lbry.sync_apply({ password: , data: response.data, blocking: true })
|
||||
}
|
||||
// return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
|
||||
return pushWallet(authToken)
|
||||
.then((response) => {
|
||||
if (!response.hash) {
|
||||
throw Error('No hash returned for sync/set.');
|
||||
}
|
||||
|
||||
return dispatch({
|
||||
type: ACTIONS.SET_SYNC_COMPLETED,
|
||||
data: { syncHash: response.hash },
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_SYNC_FAILED,
|
||||
data: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doGetSync(passedPassword?: string, callback?: (any, ?boolean) => void) {
|
||||
|
||||
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
||||
|
||||
function handleCallback(error, hasNewData) {
|
||||
if (callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Second argument passed to "doGetSync" must be a function');
|
||||
}
|
||||
|
||||
callback(error, hasNewData);
|
||||
}
|
||||
}
|
||||
|
||||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const localHash = selectSyncHash(state);
|
||||
const { lbrysync } = state;
|
||||
const { authToken, encryptedRoot } = lbrysync;
|
||||
const { pullWallet } = Lbrysync;
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_STARTED,
|
||||
});
|
||||
|
||||
const data = {};
|
||||
|
||||
Lbry.wallet_status()
|
||||
.then((status) => {
|
||||
if (status.is_locked) {
|
||||
return Lbry.wallet_unlock({ password });
|
||||
}
|
||||
|
||||
// Wallet is already unlocked
|
||||
return true;
|
||||
})
|
||||
.then((isUnlocked) => {
|
||||
if (isUnlocked) {
|
||||
return Lbry.sync_hash(); //unnec
|
||||
}
|
||||
data.unlockFailed = true;
|
||||
throw new Error();
|
||||
})
|
||||
// .then((hash?: string) => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then((hash?: string) => pullWallet(authToken))
|
||||
.then((response: any) => {
|
||||
// get data, put it in sync apply.
|
||||
const syncHash = response.hash;
|
||||
data.syncHash = syncHash;
|
||||
data.syncData = response.data;
|
||||
data.changed = response.changed || syncHash !== localHash;
|
||||
data.hasSyncedWallet = true;
|
||||
|
||||
if (response.changed) {
|
||||
return Lbry.sync_apply({ password, data: response.data, blocking: true });
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response) {
|
||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
handleCallback(null, data.changed);
|
||||
return;
|
||||
}
|
||||
|
||||
const { hash: walletHash, data: walletData } = response;
|
||||
|
||||
if (walletHash !== data.syncHash) {
|
||||
// different local hash, need to synchronise
|
||||
dispatch(doSetSync(data.syncHash, walletHash, walletData));
|
||||
}
|
||||
|
||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
handleCallback(null, data.changed);
|
||||
})
|
||||
.catch((syncAttemptError) => {
|
||||
const badPasswordError =
|
||||
syncAttemptError && syncAttemptError.data && syncAttemptError.data.name === BAD_PASSWORD_ERROR_NAME;
|
||||
|
||||
if (data.unlockFailed) {
|
||||
dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
|
||||
|
||||
if (badPasswordError) {
|
||||
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||
}
|
||||
|
||||
handleCallback(syncAttemptError);
|
||||
} else if (data.hasSyncedWallet) {
|
||||
const error = (syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_FAILED,
|
||||
data: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
if (badPasswordError) {
|
||||
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||
}
|
||||
|
||||
handleCallback(error);
|
||||
} else {
|
||||
const noWalletError = syncAttemptError && syncAttemptError.message === NO_WALLET_ERROR;
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||
data: {
|
||||
hasSyncedWallet: false,
|
||||
syncHash: null,
|
||||
// If there was some unknown error, bail
|
||||
fatalError: !noWalletError,
|
||||
},
|
||||
});
|
||||
|
||||
// user doesn't have a synced wallet
|
||||
// call sync_apply to get data to sync
|
||||
// first time sync. use any string for old hash
|
||||
if (noWalletError) {
|
||||
Lbry.sync_apply({ password })
|
||||
.then(({ hash: walletHash, data: syncApplyData }) => {
|
||||
dispatch(doSetSync('', walletHash, syncApplyData));
|
||||
handleCallback();
|
||||
})
|
||||
.catch((syncApplyError) => {
|
||||
handleCallback(syncApplyError);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -4,6 +4,10 @@ import * as SETTINGS from 'constants/settings';
|
|||
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
import Lbry from 'lbry';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import * as Lbrysync from 'lbrysync';
|
||||
import { safeStoreEncrypt, safeStoreDecrypt, getSavedPassword } from 'util/saved-passwords';
|
||||
|
||||
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
|
||||
import {
|
||||
selectSyncHash,
|
||||
|
@ -12,8 +16,7 @@ import {
|
|||
selectSyncIsLocked,
|
||||
} from 'redux/selectors/sync';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { getSavedPassword } from 'util/saved-passwords';
|
||||
import { doAnalyticsTagSync, doHandleSyncComplete } from 'redux/actions/app';
|
||||
import { doHandleSyncComplete } from 'redux/actions/app';
|
||||
import Comments from 'comments';
|
||||
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
||||
|
||||
|
@ -71,6 +74,124 @@ export function doSetDefaultAccount(success: () => void, failure: (string) => vo
|
|||
};
|
||||
}
|
||||
|
||||
export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
||||
const { fetchSaltSeed } = Lbrysync;
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
||||
});
|
||||
try {
|
||||
const saltOrError = await fetchSaltSeed(email);
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
||||
data: { email: email, saltSeed: saltOrError},
|
||||
});
|
||||
return saltOrError;
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
||||
data: { email: email, saltError: 'Not Found'},
|
||||
});
|
||||
return 'not found';
|
||||
}
|
||||
};
|
||||
|
||||
// register an email (eventually username)
|
||||
export const doLbrysyncRegister = (email: string, secrets: any, saltSeed: string) => async (dispatch: Dispatch) => {
|
||||
const { register } = Lbrysync;
|
||||
// started
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
||||
});
|
||||
const resultIfError = await register(email, secrets.providerPass, saltSeed);
|
||||
const encProviderPass = safeStoreEncrypt(secrets.providerPass);
|
||||
const encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
||||
const enctyptedRoot = safeStoreEncrypt(secrets.rootPassword);
|
||||
const registerData = {
|
||||
email,
|
||||
saltSeed,
|
||||
providerPass: encProviderPass,
|
||||
hmacKey: encHmacKey,
|
||||
rootPass: enctyptedRoot,
|
||||
};
|
||||
|
||||
if (!resultIfError) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
||||
data: registerData,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
||||
data: resultIfError,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// get token given username/password
|
||||
export const doLbrysyncAuthenticate =
|
||||
() => async (dispatch: Dispatch, getState: GetState) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_STARTED,
|
||||
});
|
||||
const state = getState();
|
||||
const { lbrysync } = state;
|
||||
const { registeredEmail: email, encryptedProviderPass } = lbrysync;
|
||||
const status = await Lbry.status();
|
||||
const { installation_id: deviceId } = status;
|
||||
const password = safeStoreDecrypt(encryptedProviderPass);
|
||||
|
||||
const { getAuthToken } = Lbrysync;
|
||||
|
||||
const result: { token?: string, error?: string } = await getAuthToken(email, password, deviceId);
|
||||
|
||||
if (result.token) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
||||
data: result.token,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_FAILED,
|
||||
data: result.error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const doGenerateSaltSeed = () => async (dispatch: Dispatch) => {
|
||||
const result = await ipcRenderer.invoke('invoke-get-salt-seed');
|
||||
return result;
|
||||
};
|
||||
|
||||
export const doDeriveSecrets = (rootPassword: string, email: string, saltSeed: string) => async (dispatch: Dispatch) =>
|
||||
{
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_STARTED,
|
||||
});
|
||||
try {
|
||||
const result = await ipcRenderer.invoke('invoke-get-secrets', rootPassword, email, saltSeed);
|
||||
|
||||
const data = {
|
||||
hmacKey: result.hmacKey,
|
||||
rootPassword,
|
||||
providerPass: result.lbryIdPassword,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_COMPLETED,
|
||||
data,
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_FAILED,
|
||||
data: {
|
||||
error: e,
|
||||
},
|
||||
});
|
||||
return { error: e.message };
|
||||
}
|
||||
};
|
||||
|
||||
export function doSetSync(oldHash: string, newHash: string, data: any) {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
|
@ -97,10 +218,18 @@ export function doSetSync(oldHash: string, newHash: string, data: any) {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
make sure
|
||||
- enabled
|
||||
- not pending
|
||||
- wallet not locked
|
||||
-
|
||||
get password
|
||||
doGetSync(password, cb)
|
||||
*/
|
||||
export const doGetSyncDesktop =
|
||||
(cb?: (any, any) => void, password?: string) => (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
||||
const getSyncPending = selectGetSyncIsPending(state);
|
||||
const setSyncPending = selectSetSyncIsPending(state);
|
||||
const syncLocked = selectSyncIsLocked(state);
|
||||
|
@ -108,31 +237,36 @@ export const doGetSyncDesktop =
|
|||
return getSavedPassword().then((savedPassword) => {
|
||||
const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword;
|
||||
|
||||
if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) {
|
||||
if (!getSyncPending && !setSyncPending && !syncLocked) {
|
||||
return dispatch(doGetSync(passwordArgument, cb));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
start regularly polling sync
|
||||
- start loop if should.
|
||||
*/
|
||||
export function doSyncLoop(noInterval?: boolean) {
|
||||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
if (!noInterval && syncTimer) clearInterval(syncTimer);
|
||||
const state = getState();
|
||||
// SHOULD SYNC
|
||||
// SHOULD SYNC?
|
||||
// syncSignedIn = selectLbrySyncSignedIn(state);
|
||||
const syncSignedIn = false;
|
||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
||||
const syncLocked = selectSyncIsLocked(state);
|
||||
// if shouldSync
|
||||
if (syncSignedIn && syncEnabled && !syncLocked) {
|
||||
// doSync
|
||||
//
|
||||
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
||||
dispatch(doAnalyticsTagSync());
|
||||
if (!noInterval) {
|
||||
syncTimer = setInterval(() => {
|
||||
const state = getState();
|
||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
||||
if (syncEnabled) {
|
||||
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
||||
dispatch(doAnalyticsTagSync());
|
||||
}
|
||||
}, SYNC_INTERVAL);
|
||||
}
|
||||
|
@ -140,6 +274,9 @@ export function doSyncLoop(noInterval?: boolean) {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
stop regularly polling sync
|
||||
*/
|
||||
export function doSyncUnsubscribe() {
|
||||
return () => {
|
||||
if (syncTimer) {
|
||||
|
@ -148,6 +285,10 @@ export function doSyncUnsubscribe() {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
make sure not locked
|
||||
|
||||
*/
|
||||
export function doGetSync(passedPassword?: string, callback?: (any, ?boolean) => void) {
|
||||
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
||||
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import { handleActions } from 'util/redux-utils';
|
||||
|
||||
const defaultState = {
|
||||
syncProvider: null,
|
||||
// reg
|
||||
registering: false,
|
||||
registeredEmail: null,
|
||||
registerError: null,
|
||||
// authtoken
|
||||
isAuthenticating: false,
|
||||
authError: null,
|
||||
authToken: null, // store this elsewhere
|
||||
// keys
|
||||
derivingKeys: false,
|
||||
encryptedHmacKey: null,
|
||||
encryptedRoot: null,
|
||||
encryptedProviderPass: null,
|
||||
// salt
|
||||
gettingSalt: false,
|
||||
saltSeed: null,
|
||||
saltError: null,
|
||||
};
|
||||
|
||||
export const lbrysyncReducer = handleActions(
|
||||
{
|
||||
// Register
|
||||
[ACTIONS.LSYNC_REGISTER_STARTED]: (state) => ({
|
||||
...state,
|
||||
registering: true,
|
||||
registerError: null,
|
||||
}),
|
||||
[ACTIONS.LSYNC_REGISTER_COMPLETED]: (state, action) => ({
|
||||
...state,
|
||||
registeredEmail: action.data.email,
|
||||
encryptedHmacKey: action.data.hmacKey,
|
||||
encryptedProviderPass: action.data.providerPass,
|
||||
encryptedRoot: action.data.rootPass,
|
||||
saltSeed: action.data.saltSeed,
|
||||
}),
|
||||
[ACTIONS.LSYNC_REGISTER_FAILED]: (state, action) => ({
|
||||
...state,
|
||||
registeredEmail: null,
|
||||
registering: false,
|
||||
registerError: action.data.error,
|
||||
}),
|
||||
// Auth
|
||||
[ACTIONS.LSYNC_AUTH_STARTED]: (state) => ({
|
||||
...state,
|
||||
isAuthenticating: true,
|
||||
authError: null,
|
||||
}),
|
||||
[ACTIONS.LSYNC_AUTH_COMPLETED]: (state, action) => ({
|
||||
...state,
|
||||
authToken: action.data,
|
||||
}),
|
||||
[ACTIONS.LSYNC_AUTH_FAILED]: (state, action) => ({
|
||||
...state,
|
||||
authError: action.data,
|
||||
isAuthenticating: false,
|
||||
}),
|
||||
// derive
|
||||
[ACTIONS.LSYNC_DERIVE_STARTED]: (state) => ({
|
||||
...state,
|
||||
derivingKeys: true,
|
||||
deriveError: null,
|
||||
}),
|
||||
[ACTIONS.LSYNC_DERIVE_COMPLETED]: (state, action) => ({
|
||||
...state,
|
||||
derivingKeys: false,
|
||||
}),
|
||||
[ACTIONS.LSYNC_DERIVE_FAILED]: (state, action) => ({
|
||||
...state,
|
||||
deriveError: action.data.error,
|
||||
derivingKeys: false,
|
||||
}),
|
||||
// salt
|
||||
[ACTIONS.LSYNC_GET_SALT_STARTED]: (state) => ({
|
||||
...state,
|
||||
gettingSalt: true,
|
||||
saltError: null,
|
||||
}),
|
||||
[ACTIONS.LSYNC_GET_SALT_COMPLETED]: (state, action) => ({
|
||||
...state,
|
||||
gettingSalt: false,
|
||||
}),
|
||||
[ACTIONS.LSYNC_GET_SALT_FAILED]: (state, action) => ({
|
||||
...state,
|
||||
saltError: action.data.error,
|
||||
gettingSalt: false,
|
||||
}),
|
||||
},
|
||||
defaultState
|
||||
);
|
|
@ -16,6 +16,25 @@ const defaultState = {
|
|||
syncLocked: false,
|
||||
hashChanged: false,
|
||||
fatalError: false,
|
||||
// lbrysync
|
||||
syncProvider: null,
|
||||
// reg
|
||||
registering: false,
|
||||
registeredEmail: null,
|
||||
registerError: null,
|
||||
// authtoken
|
||||
isAuthenticating: false,
|
||||
authError: null,
|
||||
authToken: null, // store this elsewhere
|
||||
// keys
|
||||
derivingKeys: false,
|
||||
encryptedHmacKey: null,
|
||||
encryptedRoot: null,
|
||||
encryptedProviderPass: null,
|
||||
// salt
|
||||
gettingSalt: false,
|
||||
saltSeed: null,
|
||||
saltError: null,
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SYNC_STATE_POPULATE] = (state) => {
|
||||
|
@ -107,6 +126,71 @@ reducers[ACTIONS.SYNC_FATAL_ERROR] = (state) => {
|
|||
fatalError: true,
|
||||
});
|
||||
};
|
||||
// lbrysync
|
||||
reducers[ACTIONS.LSYNC_REGISTER_STARTED] = (state) => ({
|
||||
...state,
|
||||
registering: true,
|
||||
registerError: null,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_REGISTER_COMPLETED] = (state, action) => ({
|
||||
...state,
|
||||
registeredEmail: action.data.email,
|
||||
encryptedHmacKey: action.data.hmacKey,
|
||||
encryptedProviderPass: action.data.providerPass,
|
||||
encryptedRoot: action.data.rootPass,
|
||||
saltSeed: action.data.saltSeed,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_REGISTER_FAILED] = (state, action) => ({
|
||||
...state,
|
||||
registeredEmail: null,
|
||||
registering: false,
|
||||
registerError: action.data.error,
|
||||
});
|
||||
// Auth
|
||||
reducers[ACTIONS.LSYNC_AUTH_STARTED] = (state) => ({
|
||||
...state,
|
||||
isAuthenticating: true,
|
||||
authError: null,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_AUTH_COMPLETED] = (state, action) => ({
|
||||
...state,
|
||||
authToken: action.data,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_AUTH_FAILED] = (state, action) => ({
|
||||
...state,
|
||||
authError: action.data,
|
||||
isAuthenticating: false,
|
||||
});
|
||||
// derive
|
||||
reducers[ACTIONS.LSYNC_DERIVE_STARTED] = (state) => ({
|
||||
...state,
|
||||
derivingKeys: true,
|
||||
deriveError: null,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_DERIVE_COMPLETED] = (state, action) => ({
|
||||
...state,
|
||||
derivingKeys: false,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_DERIVE_FAILED] = (state, action) => ({
|
||||
...state,
|
||||
deriveError: action.data.error,
|
||||
derivingKeys: false,
|
||||
});
|
||||
// salt
|
||||
reducers[ACTIONS.LSYNC_GET_SALT_STARTED] = (state) => ({
|
||||
...state,
|
||||
gettingSalt: true,
|
||||
saltError: null,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_GET_SALT_COMPLETED] = (state, action) => ({
|
||||
...state,
|
||||
gettingSalt: false,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_GET_SALT_FAILED] = (state, action) => ({
|
||||
...state,
|
||||
saltError: action.data.error,
|
||||
gettingSalt: false,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = (state) => state.lbrysync || {};
|
||||
|
||||
export const selectLbrySyncRegistering = createSelector(selectState, (state) => state.registering);
|
||||
export const selectLbrySyncEmail = createSelector(selectState, (state) => state.registeredEmail);
|
||||
export const selectLbrySyncRegisterError = createSelector(selectState, (state) => state.registerError);
|
||||
|
||||
export const selectLbrySyncGettingSalt = createSelector(selectState, (state) => state.gettingSalt);
|
||||
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
||||
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
||||
|
||||
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
||||
|
||||
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
||||
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
||||
|
||||
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
||||
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
||||
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
||||
export const selectLbrySyncEncryptedProviderPass = createSelector(selectState, (state) => state.encryptedProviderPass);
|
|
@ -29,3 +29,21 @@ export const selectSyncIsLocked = createSelector(selectState, state => state.syn
|
|||
export const selectPrefsReady = createSelector(selectState, state => state.prefsReady);
|
||||
|
||||
export const selectSyncFatalError = createSelector(selectState, state => state.fatalError);
|
||||
// lbrysync
|
||||
export const selectLbrySyncRegistering = createSelector(selectState, (state) => state.registering);
|
||||
export const selectLbrySyncEmail = createSelector(selectState, (state) => state.registeredEmail);
|
||||
export const selectLbrySyncRegisterError = createSelector(selectState, (state) => state.registerError);
|
||||
|
||||
export const selectLbrySyncGettingSalt = createSelector(selectState, (state) => state.gettingSalt);
|
||||
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
||||
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
||||
|
||||
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
||||
|
||||
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
||||
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
||||
|
||||
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
||||
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
||||
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
||||
export const selectLbrySyncEncryptedProviderPass = createSelector(selectState, (state) => state.encryptedProviderPass);
|
||||
|
|
Loading…
Reference in a new issue