Etebase change to login.

master
Tom Hacohen 4 years ago
parent 6d18d494a7
commit 1817fbf87d

@ -9,11 +9,13 @@
"@material-ui/lab": "^4.0.0-alpha.47", "@material-ui/lab": "^4.0.0-alpha.47",
"@material-ui/pickers": "^3.2.10", "@material-ui/pickers": "^3.2.10",
"@material-ui/styles": "^4.6.0", "@material-ui/styles": "^4.6.0",
"etebase": "^0.7.4",
"etesync": "^0.3.1", "etesync": "^0.3.1",
"fuse.js": "^5.0.9-beta", "fuse.js": "^5.0.9-beta",
"ical.js": "^1.4.0", "ical.js": "^1.4.0",
"immutable": "^4.0.0-rc.12", "immutable": "^4.0.0-rc.12",
"localforage": "^1.9.0", "localforage": "^1.9.0",
"memoizee": "^0.4.14",
"moment": "^2.27.0", "moment": "^2.27.0",
"react": "^16.13.1", "react": "^16.13.1",
"react-big-calendar": "^0.26.0", "react-big-calendar": "^0.26.0",
@ -46,6 +48,7 @@
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"@types/color": "^3.0.1", "@types/color": "^3.0.1",
"@types/jest": "^24.0.4", "@types/jest": "^24.0.4",
"@types/memoizee": "^0.4.4",
"@types/node": "^11.9.3", "@types/node": "^11.9.3",
"@types/node-rsa": "^1.0.0", "@types/node-rsa": "^1.0.0",
"@types/react": "^16.9.0", "@types/react": "^16.9.0",

@ -3,116 +3,41 @@
import * as React from "react"; import * as React from "react";
import { Action } from "redux-actions"; import { useDispatch } from "react-redux";
import Container from "./widgets/Container"; import Container from "./widgets/Container";
import ExternalLink from "./widgets/ExternalLink"; import ExternalLink from "./widgets/ExternalLink";
import SyncGate from "./SyncGate"; import SyncGate from "./SyncGate";
import LoginForm from "./components/LoginForm"; import LoginForm from "./components/LoginForm";
import EncryptionLoginForm from "./components/EncryptionLoginForm";
import { store, StoreState, CredentialsDataRemote } from "./store"; import { login } from "./store/actions";
import { deriveKey, fetchCredentials, fetchUserInfo, logout } from "./store/actions";
import * as EteSync from "etesync";
import * as C from "./constants"; import * as C from "./constants";
import SignedPagesBadge from "./images/signed-pages-badge.svg"; import SignedPagesBadge from "./images/signed-pages-badge.svg";
import LoadingIndicator from "./widgets/LoadingIndicator"; import { useCredentials } from "./credentials";
import { useCredentials, useRemoteCredentials } from "./login";
import { useSelector } from "react-redux";
function EncryptionPart(props: { credentials: CredentialsDataRemote }) {
const [fetched, setFetched] = React.useState(false);
const [userInfo, setUserInfo] = React.useState<EteSync.UserInfo>();
const [error, setError] = React.useState<Error>();
const credentials = props.credentials;
React.useEffect(() => {
store.dispatch<any>(fetchUserInfo(credentials, credentials.credentials.email)).then((fetchedUserInfo: Action<EteSync.UserInfo>) => {
setUserInfo(fetchedUserInfo.payload);
}).catch((e: Error) => {
// Do nothing.
if (e instanceof EteSync.HTTPError) {
if (e.status === 404) {
// Do nothing
} else if (e.status === 401) {
store.dispatch(logout(credentials));
} else {
setError(e);
}
}
}).finally(() => {
setFetched(true);
});
}, [credentials]);
if (!fetched) {
return <LoadingIndicator />;
}
function onEncryptionFormSubmit(encryptionPassword: string) {
const derivedAction = deriveKey(credentials.credentials.email, encryptionPassword);
if (userInfo) {
const userInfoCryptoManager = userInfo.getCryptoManager(derivedAction.payload!);
try {
userInfo.verify(userInfoCryptoManager);
} catch (e) {
setError(new EteSync.EncryptionPasswordError("Wrong encryption password"));
return;
}
}
store.dispatch(derivedAction);
}
const isNewUser = !userInfo;
return (
<Container style={{ maxWidth: "30rem" }}>
<h2>Encryption Password</h2>
{(isNewUser) ?
<div>
<h3>Welcome to EteSync!</h3>
<p>
Please set your encryption password below, and make sure you got it right, as it <em>can't</em> be recovered if lost!
</p>
</div>
:
<p>
You are logged in as <strong>{credentials.credentials.email}</strong>.
Please enter your encryption password to continue, or log out from the side menu.
</p>
}
<EncryptionLoginForm
error={error}
onSubmit={onEncryptionFormSubmit}
/>
</Container>
);
}
export default function LoginGate() { export default function LoginGate() {
const remoteCredentials = useRemoteCredentials();
const credentials = useCredentials(); const credentials = useCredentials();
const fetchCount = useSelector((state: StoreState) => state.fetchCount); const dispatch = useDispatch();
const [fetchError, setFetchError] = React.useState<Error>(); const [fetchError, setFetchError] = React.useState<Error>();
async function onFormSubmit(username: string, password: string, serviceApiUrl?: string) { async function onFormSubmit(username: string, password: string, serviceApiUrl?: string) {
serviceApiUrl = serviceApiUrl ? serviceApiUrl : C.serviceApiBase; serviceApiUrl = serviceApiUrl ? serviceApiUrl : C.serviceApiBase;
try { try {
setFetchError(undefined); setFetchError(undefined);
const ret = fetchCredentials(username, password, serviceApiUrl); const ret = login(username, password, serviceApiUrl);
await ret.payload; await ret.payload;
store.dispatch(ret); dispatch(ret);
} catch (e) { } catch (e) {
setFetchError(e); setFetchError(e);
} }
} }
if (remoteCredentials === null) { const loading = credentials === undefined;
if (!credentials) {
const style = { const style = {
isSafe: { isSafe: {
textDecoration: "none", textDecoration: "none",
@ -130,7 +55,7 @@ export default function LoginGate() {
<LoginForm <LoginForm
onSubmit={onFormSubmit} onSubmit={onFormSubmit}
error={fetchError} error={fetchError}
loading={fetchCount > 0} loading={loading}
/> />
<hr style={style.divider} /> <hr style={style.divider} />
<ExternalLink style={style.isSafe} href="https://www.etesync.com/faq/#signed-pages"> <ExternalLink style={style.isSafe} href="https://www.etesync.com/faq/#signed-pages">
@ -147,13 +72,9 @@ export default function LoginGate() {
</ul> </ul>
</Container> </Container>
); );
} else if (credentials === null) {
return (
<EncryptionPart credentials={remoteCredentials} />
);
} }
return ( return (
<SyncGate etesync={credentials} /> <SyncGate etesync={credentials as any} />
); );
} }

@ -35,7 +35,7 @@ export default function SideMenu(props: PropsType) {
const history = useHistory(); const history = useHistory();
function logoutDo() { function logoutDo() {
store.dispatch(logout(etesync!)); store.dispatch(logout(etesync! as any));
props.onCloseDrawerRequest(); props.onCloseDrawerRequest();
} }

@ -1,104 +0,0 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from "react";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
interface FormErrors {
errorEncryptionPassword?: string;
}
class EncryptionLoginForm extends React.PureComponent {
public state: {
errors: FormErrors;
encryptionPassword: string;
};
public props: {
onSubmit: (encryptionPassword: string) => void;
loading?: boolean;
error?: Error;
};
constructor(props: any) {
super(props);
this.state = {
errors: {},
encryptionPassword: "",
};
this.generateEncryption = this.generateEncryption.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
public handleInputChange(event: React.ChangeEvent<any>) {
const name = event.target.name;
const value = event.target.value;
this.setState({
[name]: value,
});
}
public generateEncryption(e: any) {
e.preventDefault();
const encryptionPassword = this.state.encryptionPassword;
const errors: FormErrors = {};
const fieldRequired = "This field is required!";
if (!encryptionPassword) {
errors.errorEncryptionPassword = fieldRequired;
}
if (Object.keys(errors).length) {
this.setState({ errors });
return;
} else {
this.setState({ errors: {} });
}
this.props.onSubmit(encryptionPassword);
}
public render() {
const styles = {
form: {
},
submit: {
marginTop: 40,
textAlign: "right" as any,
},
};
return (
<React.Fragment>
{(this.props.error) && (<div>Error! {this.props.error.message}</div>)}
<form style={styles.form} onSubmit={this.generateEncryption}>
<TextField
type="password"
autoFocus
error={!!this.state.errors.errorEncryptionPassword}
helperText={this.state.errors.errorEncryptionPassword}
label="Encryption Password"
name="encryptionPassword"
value={this.state.encryptionPassword}
onChange={this.handleInputChange}
/>
<div style={styles.submit}>
<Button
type="submit"
variant="contained"
color="secondary"
disabled={this.props.loading}
>
{this.props.loading ? "Loading…" : "Continue"}
</Button>
</div>
</form>
</React.Fragment>
);
}
}
export default EncryptionLoginForm;

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: © 2017 Etebase Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { useSelector } from "react-redux";
import { createSelector } from "reselect";
import * as Etebase from "etebase";
import * as store from "./store";
import { usePromiseMemo } from "./helpers";
export const credentialsSelector = createSelector(
(state: store.StoreState) => state.credentials2.storedSession,
(storedSession) => {
if (storedSession) {
return Etebase.Account.restore(storedSession);
} else {
return Promise.resolve(null);
}
}
);
export function useCredentials() {
const credentialsPromise = useSelector(credentialsSelector);
return usePromiseMemo(credentialsPromise, [credentialsPromise]);
}

@ -0,0 +1,75 @@
import memoize from "memoizee";
import * as Etebase from "etebase";
import { useSelector } from "react-redux";
import { StoreState } from "./store";
import { CacheCollectionsData, CacheItems, CacheItemsData } from "./store/reducers";
import { usePromiseMemo } from "./helpers";
export const getCollections = memoize(async function (cachedCollections: CacheCollectionsData, etebase: Etebase.Account) {
const colMgr = getCollectionManager(etebase);
const ret: Etebase.Collection[] = [];
for (const cached of cachedCollections.values()) {
ret.push(await colMgr.cacheLoad(Etebase.fromBase64(cached)));
}
return ret;
}, { length: 1 });
export const getCollectionsByType = memoize(async function (cachedCollections: CacheCollectionsData, colType: string, etebase: Etebase.Account) {
const collections = await getCollections(cachedCollections, etebase);
const ret: Etebase.Collection[] = [];
for (const col of collections) {
const meta = await col.getMeta();
if (meta.type === colType) {
ret.push(col);
}
}
return ret;
}, { length: 2 });
export const getItems = memoize(async function (cachedItems: CacheItems, itemMgr: Etebase.CollectionItemManager) {
const ret = new Map<string, Etebase.CollectionItem>();
for (const cached of cachedItems.values()) {
const item = await itemMgr.cacheLoad(Etebase.fromBase64(cached));
ret.set(item.uid, item);
}
return ret;
}, { length: 1 });
export const getItemsByType = memoize(async function (cachedCollections: CacheCollectionsData, cachedItems: CacheItemsData, colType: string, etebase: Etebase.Account) {
const colMgr = getCollectionManager(etebase);
const collections = await getCollectionsByType(cachedCollections, colType, etebase);
const ret = new Map<string, Map<string, Etebase.CollectionItem>>();
for (const col of collections) {
const itemMgr = colMgr.getItemManager(col);
const cachedColItems = cachedItems.get(col.uid);
if (cachedColItems) {
const items = await getItems(cachedColItems, itemMgr);
ret.set(col.uid, items);
}
}
return ret;
}, { length: 3 });
export const getCollectionManager = memoize(function (etebase: Etebase.Account) {
return etebase.getCollectionManager();
});
// React specific stuff
export function useCollections(etebase: Etebase.Account, colType: string) {
const cachedCollections = useSelector((state: StoreState) => state.cache2.collections);
return usePromiseMemo(
getCollectionsByType(cachedCollections, colType, etebase),
[etebase, cachedCollections, colType]
);
}
export function useItems(etebase: Etebase.Account, colType: string) {
const cachedCollections = useSelector((state: StoreState) => state.cache2.collections);
const cachedItems = useSelector((state: StoreState) => state.cache2.items);
return usePromiseMemo(
getItemsByType(cachedCollections, cachedItems, colType, etebase),
[etebase, cachedCollections, cachedItems, colType]
);
}

@ -120,3 +120,23 @@ export function mapPriority(priority: number): TaskPriorityType {
return TaskPriorityType.Undefined; return TaskPriorityType.Undefined;
} }
} }
export function usePromiseMemo<T>(promise: Promise<T> | undefined | null, deps: React.DependencyList, initial: T | undefined = undefined): T | undefined {
const [val, setVal] = React.useState<T>((promise as any)._returnedValue ?? initial);
React.useEffect(() => {
let cancel = false;
if (promise === undefined || promise === null) {
return undefined;
}
promise.then((val) => {
(promise as any)._returnedValue = val;
if (!cancel) {
setVal(val);
}
});
return () => {
cancel = true;
};
}, [...deps, promise]);
return val;
}

@ -3,10 +3,12 @@
import { Action, createAction, createActions } from "redux-actions"; import { Action, createAction, createActions } from "redux-actions";
import * as Etebase from "etebase";
import * as EteSync from "etesync"; import * as EteSync from "etesync";
import { UserInfo } from "etesync"; import { UserInfo } from "etesync";
import { CredentialsData, CredentialsDataRemote, EntriesData, SettingsType } from "./"; import { CredentialsData, EntriesData, SettingsType } from "./";
export const { fetchCredentials } = createActions({ export const { fetchCredentials } = createActions({
FETCH_CREDENTIALS: (username: string, password: string, server: string) => { FETCH_CREDENTIALS: (username: string, password: string, server: string) => {
@ -32,21 +34,6 @@ export const { fetchCredentials } = createActions({
}, },
}); });
export const logout = createAction(
"LOGOUT",
(etesync: CredentialsDataRemote) => {
(async () => {
const authenticator = new EteSync.Authenticator(etesync.serviceApiUrl);
try {
await authenticator.invalidateToken(etesync.credentials.authToken);
} catch {
// Ignore for now. It usually means the token was a legacy one.
}
})();
return; // We are not waiting on the above on purpose for now, just invalidate the token in the background
}
);
export const { deriveKey } = createActions({ export const { deriveKey } = createActions({
DERIVE_KEY: (username: string, encryptionPassword: string) => { DERIVE_KEY: (username: string, encryptionPassword: string) => {
return EteSync.deriveKey(username, encryptionPassword); return EteSync.deriveKey(username, encryptionPassword);
@ -60,13 +47,87 @@ export const resetKey = createAction(
} }
); );
export const login = (username: string, password: string, encryptionPassword: string, server: string) => { export const logout = createAction(
return (dispatch: any) => { "LOGOUT",
dispatch(fetchCredentials(username, password, server)).then(() => async (etebase: Etebase.Account) => {
dispatch(deriveKey(username, encryptionPassword)) await etebase.logout();
); }
);
export const login = createAction(
"LOGIN",
async (username: string, password: string, server: string) => {
const etebase = await Etebase.Account.login(username, password, server);
return etebase.save();
}
);
export const setCacheCollection = createAction(
"SET_CACHE_COLLECTION",
async (colMgr: Etebase.CollectionManager, col: Etebase.Collection) => {
return Etebase.toBase64(await colMgr.cacheSave(col));
},
(_colMgr: Etebase.CollectionManager, col: Etebase.Collection) => {
return col.uid;
}
);
export const unsetCacheCollection = createAction(
"UNSET_CACHE_COLLECTION",
(_colMgr: Etebase.CollectionManager, colUid: string) => {
return colUid;
}
);
export const setCacheItem = createAction(
"SET_CACHE_ITEM",
async (_col: Etebase.Collection, itemMgr: Etebase.CollectionItemManager, item: Etebase.CollectionItem) => {
return Etebase.toBase64(await itemMgr.cacheSave(item));
},
(col: Etebase.Collection, _itemMgr: Etebase.CollectionItemManager, item: Etebase.CollectionItem) => {
return {
colUid: col.uid,
itemUid: item.uid,
}; };
}; }
);
export const unsetCacheItem = createAction(
"UNSET_CACHE_ITEM",
(_colUid: string, _itemMgr: Etebase.CollectionItemManager, itemUid: string) => {
return itemUid;
},
(colUid: string, _itemMgr: Etebase.CollectionItemManager, itemUid: string) => {
return {
colUid,
itemUid,
};
}
);
export const setSyncCollection = createAction(
"SET_SYNC_COLLECTION",
(colUid: string, stoken: string) => {
return {
colUid,
stoken,
};
}
);
export const setSyncGeneral = createAction(
"SET_SYNC_GENERAL",
(stoken: string | null) => {
return stoken;
}
);
export const performSync = createAction(
"PERFORM_SYNC",
(syncPromise: Promise<any>) => {
return syncPromise;
}
);
export const { fetchListJournal } = createActions({ export const { fetchListJournal } = createActions({
FETCH_LIST_JOURNAL: (etesync: CredentialsData) => { FETCH_LIST_JOURNAL: (etesync: CredentialsData) => {

@ -13,13 +13,24 @@ import {
JournalsData, EntriesData, UserInfoData, JournalsData, EntriesData, UserInfoData,
CredentialsDataRemote, SettingsType, CredentialsDataRemote, SettingsType,
fetchCount, journals, entries, credentials, userInfo, settingsReducer, encryptionKeyReducer, errorsReducer, fetchCount, journals, entries, credentials, userInfo, settingsReducer, encryptionKeyReducer, errorsReducer,
syncGeneral, syncCollections, collections, items,
SyncGeneralData, SyncCollectionsData, CacheCollectionsData, CacheItemsData, CredentialsData2,
} from "./reducers"; } from "./reducers";
export interface StoreState { export interface StoreState {
fetchCount: number; fetchCount: number;
credentials: CredentialsDataRemote; credentials: CredentialsDataRemote;
credentials2: CredentialsData2;
settings: SettingsType; settings: SettingsType;
encryptionKey: {key: string}; encryptionKey: {key: string};
sync: {
collections: SyncCollectionsData;
general: SyncGeneralData;
};
cache2: {
collections: CacheCollectionsData;
items: CacheItemsData;
};
cache: { cache: {
journals: JournalsData; journals: JournalsData;
entries: EntriesData; entries: EntriesData;
@ -60,6 +71,12 @@ const credentialsPersistConfig = {
migrate: createMigrate(credentialsMigrations, { debug: false }), migrate: createMigrate(credentialsMigrations, { debug: false }),
}; };
const credentials2PersistConfig = {
key: "credentials2",
version: 0,
storage: localforage,
};
const encryptionKeyPersistConfig = { const encryptionKeyPersistConfig = {
key: "encryptionKey", key: "encryptionKey",
storage: session, storage: session,
@ -183,11 +200,71 @@ const cachePersistConfig = {
migrate: createMigrate(cacheMigrations, { debug: false }), migrate: createMigrate(cacheMigrations, { debug: false }),
}; };
const syncSerialize = (state: any, key: string | number) => {
if (key === "collections") {
return state.toJS();
}
return state;
};
const syncDeserialize = (state: any, key: string | number) => {
if (key === "collections") {
return ImmutableMap(state);
}
return state;
};
const syncPersistConfig = {
key: "sync",
storage: localforage,
transforms: [createTransform(syncSerialize, syncDeserialize)],
};
const cache2Serialize = (state: any, key: string | number) => {
if (key === "collections") {
return state.toJS();
} else if (key === "items") {
return state.toJS();
}
return state;
};
const cache2Deserialize = (state: any, key: string | number) => {
if (key === "collections") {
return ImmutableMap(state);
} else if (key === "items") {
return ImmutableMap(state).map((item: any) => {
return ImmutableMap(item);
});
}
return state;
};
const cache2PersistConfig = {
key: "cache2",
version: 0,
storage: localforage,
transforms: [createTransform(cache2Serialize, cache2Deserialize)] as any,
};
const reducers = combineReducers({ const reducers = combineReducers({
fetchCount, fetchCount,
settings: persistReducer(settingsPersistConfig, settingsReducer), settings: persistReducer(settingsPersistConfig, settingsReducer),
credentials: persistReducer(credentialsPersistConfig, credentials), credentials: persistReducer(credentialsPersistConfig, credentials),
credentials2: persistReducer(credentials2PersistConfig, credentials),
encryptionKey: persistReducer(encryptionKeyPersistConfig, encryptionKeyReducer), encryptionKey: persistReducer(encryptionKeyPersistConfig, encryptionKeyReducer),
sync: persistReducer(syncPersistConfig, combineReducers({
collections: syncCollections,
general: syncGeneral,
})),
cache2: persistReducer(cache2PersistConfig, combineReducers({
collections,
items,
})),
cache: persistReducer(cachePersistConfig, combineReducers({ cache: persistReducer(cachePersistConfig, combineReducers({
entries, entries,
journals, journals,

@ -10,6 +10,13 @@ import * as EteSync from "etesync";
import * as actions from "./actions"; import * as actions from "./actions";
export type JournalsData = ImmutableMap<string, EteSync.Journal>;
export type EntriesListData = List<EteSync.Entry>;
export type EntriesData = ImmutableMap<string, EntriesListData>;
export type UserInfoData = EteSync.UserInfo;
export interface CredentialsDataRemote { export interface CredentialsDataRemote {
serviceApiUrl: string; serviceApiUrl: string;
credentials: EteSync.Credentials; credentials: EteSync.Credentials;
@ -19,49 +26,131 @@ export interface CredentialsData extends CredentialsDataRemote {
encryptionKey: string; encryptionKey: string;
} }
export type JournalsData = ImmutableMap<string, EteSync.Journal>; interface BaseModel {
uid: string;
}
export type EntriesListData = List<EteSync.Entry>; export interface SyncCollectionsEntryData extends BaseModel {
export type EntriesData = ImmutableMap<string, EntriesListData>; stoken: string;
}
export type UserInfoData = EteSync.UserInfo; export type SyncCollectionsData = ImmutableMap<string, SyncCollectionsEntryData>;
export type CacheItem = string;
export type CacheItems = ImmutableMap<string, CacheItem>;
export type CacheItemsData = ImmutableMap<string, CacheItems>;
export type CacheCollection = CacheItem;
export type CacheCollectionsData = ImmutableMap<string, CacheCollection>;
export type SyncGeneralData = {
stoken: string | null;
lastSyncDate: Date;
};
export interface CredentialsData2 {
storedSession?: string;
}
export const credentials = handleActions(
{
[actions.login.toString()]: (
state: CredentialsData2, action: Action<string>) => {
if (action.error) {
return state;
} else if (action.payload === undefined) {
return state;
} else {
return {
storedSession: action.payload,
};
}
},
[actions.logout.toString()]: (_state: CredentialsData2, _action: any) => {
return { storedSession: undefined };
},
},
{ storedSession: undefined }
);
export const encryptionKeyReducer = handleActions( export const encryptionKeyReducer = handleActions(
{ {
[actions.deriveKey.toString()]: (_state: { key: string | null }, action: any) => ( },
{ key: action.payload }
),
[actions.resetKey.toString()]: (_state: { key: string | null }, _action: any) => (
{ key: null } { key: null }
), );
[actions.logout.toString()]: (_state: { key: string | null }, _action: any) => {
return { out: true, key: null }; export const syncCollections = handleActions(
{
[actions.setSyncCollection.toString()]: (state: SyncCollectionsData, action: Action<SyncCollectionsEntryData>) => {
if (action.payload !== undefined) {
return state.set(action.payload.uid, action.payload);
}
return state;
}, },
[actions.logout.toString()]: (state: SyncCollectionsData, _action: any) => {
return state.clear();
}, },
{ key: null } },
ImmutableMap({})
); );
export const credentials = handleActions( export const syncGeneral = handleActions(
{ {
[actions.fetchCredentials.toString()]: ( [actions.setSyncGeneral.toString()]: (state: SyncGeneralData, action: Action<string | null | undefined>) => {
state: CredentialsDataRemote, action: any) => { if (action.payload !== undefined) {
if (action.error) { return {
stoken: action.payload,
lastSyncDate: new Date(),
};
}
return state; return state;
} else if (action.payload === undefined) { },
[actions.logout.toString()]: (_state: SyncGeneralData, _action: any) => {
return {};
},
},
{}
);
export const collections = handleActions(
{
[actions.setCacheCollection.toString()]: (state: CacheCollectionsData, action: ActionMeta<CacheCollection, { colUid: string }>) => {
if (action.payload !== undefined) {
return state.set(action.meta.colUid, action.payload);
}
return state; return state;
} else { },
const { [actions.unsetCacheCollection.toString()]: (state: CacheCollectionsData, action: ActionMeta<string, { colUid: string }>) => {
encryptionKey, // We don't want to set encryption key here. if (action.payload !== undefined) {
...payload return state.remove(action.meta.colUid);
} = action.payload;
return payload;
} }
return state;
}, },
[actions.logout.toString()]: (_state: CredentialsDataRemote, _action: any) => {
return {};
}, },
ImmutableMap({})
);
export const items = handleActions(
{
[actions.setCacheItem.toString()]: (state: CacheItemsData, action: ActionMeta<CacheItem, { colUid: string, itemUid: string }>) => {
if (action.payload !== undefined) {
return state.setIn([action.meta.colUid, action.meta.itemUid], action.payload);
}
return state;
}, },
{} as CredentialsDataRemote [actions.unsetCacheItem.toString()]: (state: CacheItemsData, action: ActionMeta<string, { colUid: string, itemUid: string }>) => {
if (action.payload !== undefined) {
return state.removeIn([action.meta.colUid, action.meta.itemUid]);
}
return state;
},
[actions.unsetCacheCollection.toString()]: (state: CacheItemsData, action: ActionMeta<string, { colUid: string }>) => {
if (action.payload !== undefined) {
return state.remove(action.meta.colUid);
}
return state;
},
},
ImmutableMap({})
); );
function entriesListSetExtend( function entriesListSetExtend(

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: © 2019 EteSync Authors
// SPDX-License-Identifier: GPL-3.0-only
import * as EteSync from "etesync";
export const deriveKey = EteSync.deriveKey;

@ -1477,6 +1477,11 @@
call-me-maybe "^1.0.1" call-me-maybe "^1.0.1"
glob-to-regexp "^0.3.0" glob-to-regexp "^0.3.0"
"@msgpack/msgpack@^1.12.2":
version "1.12.2"
resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-1.12.2.tgz#6a22e99a49b131a8789053d0b0903834552da36f"
integrity sha512-Vwhc3ObxmDZmA5hY8mfsau2rJ4vGPvzbj20QSZ2/E1GDPF61QVyjLfNHak9xmel6pW4heRt3v1fHa6np9Ehfeg==
"@nodelib/fs.stat@^1.1.2": "@nodelib/fs.stat@^1.1.2":
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
@ -1766,6 +1771,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
"@types/memoizee@^0.4.4":
version "0.4.4"
resolved "https://registry.yarnpkg.com/@types/memoizee/-/memoizee-0.4.4.tgz#a8a5e917ef70c79523b8b8d57f529e49616a760c"
integrity sha512-c9+1g6+6vEqcw5UuM0RbfQV0mssmZcoG9+hNC5ptDCsv4G+XJW1Z4pE13wV5zbc9e0+YrDydALBTiD3nWG1a3g==
"@types/minimatch@*": "@types/minimatch@*":
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@ -4355,7 +4365,7 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.50: es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.53" version "0.10.53"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1"
integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==
@ -4364,7 +4374,7 @@ es5-ext@^0.10.35, es5-ext@^0.10.50:
es6-symbol "~3.1.3" es6-symbol "~3.1.3"
next-tick "~1.0.0" next-tick "~1.0.0"
es6-iterator@2.0.3, es6-iterator@~2.0.3: es6-iterator@2.0.3, es6-iterator@^2.0.3, es6-iterator@~2.0.3:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
@ -4381,6 +4391,16 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3:
d "^1.0.1" d "^1.0.1"
ext "^1.1.2" ext "^1.1.2"
es6-weak-map@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53"
integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==
dependencies:
d "1"
es5-ext "^0.10.46"
es6-iterator "^2.0.3"
es6-symbol "^3.1.1"
escalade@^3.0.1: escalade@^3.0.1:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
@ -4636,6 +4656,18 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
etebase@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/etebase/-/etebase-0.7.4.tgz#ba7b952ce05a26819610ead153d17d1247c6ba64"
integrity sha512-Efxt7wNCNi5YH5Q7u01pX2D2FGx6Bw3i2l/WCKlgEQ/GGOzoWk0kaIAt5YmwA5oLtXc9pEhujMXWT7ocTy+jDw==
dependencies:
"@msgpack/msgpack" "^1.12.2"
libsodium-wrappers "^0.7.6"
node-fetch "^2.6.0"
urijs "^1.19.1"
optionalDependencies:
react-native-sodium "^0.3.8"
etesync@^0.3.1: etesync@^0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/etesync/-/etesync-0.3.1.tgz#2af257d5617679177d93fb8f6bdaf64462aa15e0" resolved "https://registry.yarnpkg.com/etesync/-/etesync-0.3.1.tgz#2af257d5617679177d93fb8f6bdaf64462aa15e0"
@ -4645,6 +4677,14 @@ etesync@^0.3.1:
sjcl "git+https://github.com/etesync/sjcl" sjcl "git+https://github.com/etesync/sjcl"
urijs "^1.19.1" urijs "^1.19.1"
event-emitter@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=
dependencies:
d "1"
es5-ext "~0.10.14"
eventemitter3@^4.0.0: eventemitter3@^4.0.0:
version "4.0.4" version "4.0.4"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384"
@ -6089,6 +6129,11 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-promise@^2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
is-regex@^1.0.4, is-regex@^1.1.0: is-regex@^1.0.4, is-regex@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
@ -6965,6 +7010,18 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2" prelude-ls "~1.1.2"
type-check "~0.3.2" type-check "~0.3.2"
libsodium-wrappers@^0.7.6:
version "0.7.8"
resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.8.tgz#d95cdf3e7236c2aef76844bf8e1929ba9eef3e9e"
integrity sha512-PDhPWXBqd/SaqAFUBgH2Ux7b3VEEJgyD6BQB+VdNFJb9PbExGr/T/myc/MBoSvl8qLzfm0W0IVByOQS5L1MrCg==
dependencies:
libsodium "0.7.8"
libsodium@0.7.8:
version "0.7.8"
resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.8.tgz#fbd12247b7b1353f88d8de1cbc66bc1a07b2e008"
integrity sha512-/Qc+APf0jbeWSaeEruH0L1/tbbT+sbf884ZL0/zV/0JXaDPBzYkKbyb/wmxMHgAHzm3t6gqe7bOOXAVwfqVikQ==
lie@3.1.1: lie@3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
@ -7129,6 +7186,13 @@ lru-cache@^5.1.1:
dependencies: dependencies:
yallist "^3.0.2" yallist "^3.0.2"
lru-queue@0.1:
version "0.1.0"
resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3"
integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=
dependencies:
es5-ext "~0.10.2"
make-dir@^2.0.0, make-dir@^2.1.0: make-dir@^2.0.0, make-dir@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@ -7213,6 +7277,20 @@ memoize-one@^5.1.1:
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
memoizee@^0.4.14:
version "0.4.14"
resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57"
integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==
dependencies:
d "1"
es5-ext "^0.10.45"
es6-weak-map "^2.0.2"
event-emitter "^0.3.5"
is-promise "^2.1"
lru-queue "0.1"
next-tick "1"
timers-ext "^0.1.5"
memory-fs@^0.4.1: memory-fs@^0.4.1:
version "0.4.1" version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@ -7516,6 +7594,11 @@ neo-async@^2.5.0, neo-async@^2.6.1:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next-tick@1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
next-tick@~1.0.0: next-tick@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
@ -7534,6 +7617,11 @@ no-case@^3.0.3:
lower-case "^2.0.1" lower-case "^2.0.1"
tslib "^1.10.0" tslib "^1.10.0"
node-fetch@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-forge@0.9.0: node-forge@0.9.0:
version "0.9.0" version "0.9.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
@ -9252,6 +9340,11 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-native-sodium@^0.3.8:
version "0.3.8"
resolved "https://registry.yarnpkg.com/react-native-sodium/-/react-native-sodium-0.3.8.tgz#ccb86a67d2cd6856be227ed962ffc019b32e7642"
integrity sha512-G+ZrNUr8yWPOGYSzM+Gc+5wKeLcQBlN7SrFjt9JzbeuNM+cEJlj4ntdoaMe43cciWVPr4yQIpFOu2Qapnk5HRQ==
react-overlays@^2.0.0-0: react-overlays@^2.0.0-0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-2.1.1.tgz#ffe2090c4a10da6b8947a1c7b1a67d0457648a0d" resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-2.1.1.tgz#ffe2090c4a10da6b8947a1c7b1a67d0457648a0d"
@ -10776,6 +10869,14 @@ timers-browserify@^2.0.4:
dependencies: dependencies:
setimmediate "^1.0.4" setimmediate "^1.0.4"
timers-ext@^0.1.5:
version "0.1.7"
resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6"
integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==
dependencies:
es5-ext "~0.10.46"
next-tick "1"
timsort@^0.3.0: timsort@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"

Loading…
Cancel
Save