Split the login credentials and the encryption key.

We do this so we can persist the login info to local storage, but
still keep the encryption key safe.

This version will fails if you clear the session storage. This will be
fixed in the next commit.
master
Tom Hacohen 7 years ago
parent c7e508b890
commit 8d3a06cdfd

@ -2,6 +2,7 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { BrowserRouter } from 'react-router-dom';
import { createSelector } from 'reselect';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { amber500, amber700, lightBlue500, darkBlack, white } from 'material-ui/styles/colors';
@ -130,7 +131,7 @@ class App extends React.PureComponent {
};
props: {
credentials?: store.CredentialsType;
credentials: store.CredentialsType;
};
constructor(props: any) {
@ -170,7 +171,7 @@ class App extends React.PureComponent {
<SideMenu etesync={credentials} onCloseDrawerRequest={this.closeDrawer} />
</Drawer>
<LoginGate />
<LoginGate credentials={this.props.credentials} />
</div>
</BrowserRouter>
</MuiThemeProvider>
@ -178,9 +179,30 @@ class App extends React.PureComponent {
}
}
const credentialsSelector = createSelector(
(state: store.StoreState) => state.credentials.value,
(state: store.StoreState) => state.credentials.error,
(state: store.StoreState) => state.credentials.fetching,
(state: store.StoreState) => state.encryptionKey.key,
(value, error, fetching, encryptionKey) => {
if (value === null) {
return {value, error, fetching};
}
return {
error: error,
fetching: fetching,
value: {
...value,
encryptionKey: encryptionKey,
}
};
}
);
const mapStateToProps = (state: store.StoreState) => {
return {
credentials: state.credentials,
credentials: credentialsSelector(state),
};
};

@ -1,18 +1,16 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import Container from './widgets/Container';
import ExternalLink from './widgets/ExternalLink';
import SyncGate from './SyncGate';
import LoginForm from './components/LoginForm';
import { store, StoreState, CredentialsType } from './store';
import { store, CredentialsType } from './store';
import { fetchCredentials } from './store/actions';
import * as C from './constants';
class Root extends React.PureComponent {
class Root extends React.Component {
props: {
credentials: CredentialsType;
};
@ -65,13 +63,4 @@ class Root extends React.PureComponent {
}
}
const mapStateToProps = (state: StoreState) => {
return {
credentials: state.credentials,
};
};
// FIXME: withRouter is only needed here because of https://github.com/ReactTraining/react-router/issues/5795
export default withRouter(connect(
mapStateToProps
)(Root));
export default Root;

@ -6,13 +6,14 @@ import { createLogger } from 'redux-logger';
import promiseMiddleware from './promise-middleware';
import reducers from './reducers';
import { CredentialsType, JournalsType, EntriesType } from './reducers';
import { CredentialsTypeRemote, JournalsType, EntriesType } from './reducers';
export { CredentialsType, CredentialsData, JournalsType, JournalsData, EntriesType, EntriesData } from './reducers';
export interface StoreState {
fetchCount: number;
credentials: CredentialsType;
credentials: CredentialsTypeRemote;
encryptionKey: {key: string};
cache: {
journals: JournalsType;
entries: EntriesType;

@ -17,9 +17,12 @@ interface FetchTypeInterface<T> {
error?: Error;
}
export interface CredentialsData {
export interface CredentialsDataRemote {
serviceApiUrl: string;
credentials: EteSync.Credentials;
}
export interface CredentialsData extends CredentialsDataRemote {
encryptionKey: string;
}
@ -33,6 +36,7 @@ function fetchTypeRecord<T>() {
}
export type CredentialsType = FetchType<CredentialsData>;
export type CredentialsTypeRemote = FetchType<CredentialsDataRemote>;
export type JournalsData = List<EteSync.Journal>;
@ -47,23 +51,6 @@ const EntriesFetchRecord = fetchTypeRecord<EntriesData>();
export type EntriesTypeImmutable = Map<string, Record<FetchType<EntriesData>>>;
export type EntriesType = Map<string, FetchType<EntriesData>>;
function credentialsIdentityReducer(state: CredentialsType = {value: null}, action: any, extend: boolean = false) {
if (action.error) {
return {
value: null,
error: action.payload,
};
} else {
const fetching = (action.payload === undefined) ? true : undefined;
const payload = (action.payload === undefined) ? null : action.payload;
let value = payload;
return {
fetching,
value,
};
}
}
function fetchTypeIdentityReducer(
state: Record<FetchType<any>> = fetchTypeRecord<any>()(), action: any, extend: boolean = false) {
if (action.error) {
@ -91,10 +78,50 @@ function fetchTypeIdentityReducer(
}
}
const encryptionKeyReducer = handleActions(
{
[actions.fetchCredentials.toString()]: (
state: {key: string | null}, action: any) => {
if (action.error) {
return {key: null};
} else if (action.payload === undefined) {
return {key: null};
} else {
return {key: action.payload.encryptionKey};
}
},
[actions.logout.toString()]: (state: {key: string | null}, action: any) => {
return {out: true, key: null};
},
},
{key: null}
);
const credentials = handleActions(
{
[actions.fetchCredentials.toString()]: credentialsIdentityReducer,
[actions.logout.toString()]: (state: CredentialsType, action: any) => {
[actions.fetchCredentials.toString()]: (
state: CredentialsTypeRemote, action: any, extend: boolean = false) => {
if (action.error) {
return {
value: null,
error: action.payload,
};
} else if (action.payload === undefined) {
return {
fetching: true,
value: null,
};
} else {
const {
encryptionKey, // We don't want to set encryption key here.
...payload
} = action.payload;
return {
value: payload,
};
}
},
[actions.logout.toString()]: (state: CredentialsTypeRemote, action: any) => {
return {out: true, value: null};
},
},
@ -159,10 +186,15 @@ const fetchCount = handleAction(
const credentialsPersistConfig = {
key: 'credentials',
storage: session,
storage: localforage,
whitelist: ['value'],
};
const encryptionKeyPersistConfig = {
key: 'encryptionKey',
storage: session,
};
const journalsSerialize = (state: JournalsData) => {
if (state === null) {
return null;
@ -240,6 +272,7 @@ const cachePersistConfig = {
const reducers = combineReducers({
fetchCount,
credentials: persistReducer(credentialsPersistConfig, credentials),
encryptionKey: persistReducer(encryptionKeyPersistConfig, encryptionKeyReducer),
cache: persistReducer(cachePersistConfig, combineReducers({
entries,
journals,

Loading…
Cancel
Save