diff --git a/src/api/Crypto.test.ts b/src/api/Crypto.test.ts index 8b8becf..24fcb8d 100644 --- a/src/api/Crypto.test.ts +++ b/src/api/Crypto.test.ts @@ -3,6 +3,9 @@ import { USER, PASSWORD, keyBase64 } from './TestConstants'; import { stringToByteArray } from './Helpers'; +import * as sjcl from 'sjcl'; +sjcl.random.addEntropy('seedForTheTests', 1024, 'FakeSeed'); + it('Derive key', () => { const derived = deriveKey(USER, PASSWORD); expect(derived).toBe(keyBase64); diff --git a/src/api/EteSync.test.ts b/src/api/EteSync.test.ts index 311e32a..60f9060 100644 --- a/src/api/EteSync.test.ts +++ b/src/api/EteSync.test.ts @@ -6,6 +6,9 @@ const testApiBase = 'http://localhost:8000'; import { USER, PASSWORD, keyBase64 } from './TestConstants'; +sjcl.random.addEntropy('seedForTheTests', 1024, 'FakeSeed'); + + let credentials: EteSync.Credentials; beforeEach(async () => { diff --git a/src/api/EteSync.ts b/src/api/EteSync.ts index 519d4a2..b14341f 100644 --- a/src/api/EteSync.ts +++ b/src/api/EteSync.ts @@ -4,7 +4,7 @@ import URI from 'urijs'; import * as Constants from './Constants'; import { byte, base64, stringToByteArray } from './Helpers'; -import { CryptoManager, AsymmetricKeyPair, HMAC_SIZE_BYTES } from './Crypto'; +import { CryptoManager, AsymmetricCryptoManager, AsymmetricKeyPair, HMAC_SIZE_BYTES } from './Crypto'; export { CryptoManager, AsymmetricCryptoManager, AsymmetricKeyPair, deriveKey, genUid } from './Crypto'; type URI = typeof URI; @@ -83,7 +83,7 @@ class BaseItem { protected _content?: object; constructor() { - this._json = {} as any; + this._json = {} as T; } public deserialize(json: T) { @@ -131,9 +131,13 @@ export interface JournalJson extends BaseJson { } export class Journal extends BaseJournal { - constructor(version: number = Constants.CURRENT_VERSION) { + constructor(initial?: Partial, version: number = Constants.CURRENT_VERSION) { super(); - this._json.version = version; + this._json = { + ...this._json, + version, + ...initial, + }; } get key(): byte[] | undefined { @@ -152,6 +156,16 @@ export class Journal extends BaseJournal { return this._json.version; } + public getCryptoManager(derived: string, keyPair: AsymmetricKeyPair) { + if (this.key) { + const asymmetricCryptoManager = new AsymmetricCryptoManager(keyPair); + const derivedJournalKey = asymmetricCryptoManager.decryptBytes(this.key); + return CryptoManager.fromDerivedKey(derivedJournalKey, this.version); + } else { + return new CryptoManager(derived, this.uid, this.version); + } + } + public setInfo(cryptoManager: CryptoManager, info: CollectionInfo) { this._json.uid = info.uid; this._content = info; @@ -270,6 +284,10 @@ export class UserInfo extends BaseItem { return ret; } + public getCryptoManager(derived: string) { + return new CryptoManager(derived, 'userInfo', this.version); + } + public setKeyPair(cryptoManager: CryptoManager, keyPair: AsymmetricKeyPair) { this._json.pubkey = sjcl.codec.base64.fromBits(sjcl.codec.bytes.toBits(keyPair.publicKey)); this._content = keyPair.privateKey; @@ -323,14 +341,16 @@ class BaseNetwork { this.apiBase = URI(apiBase).normalize(); } - // FIXME: Get the correct type for extra - public newCall(segments: string[] = [], extra: any = {}, _apiBase: URI = this.apiBase): Promise { + public newCall(segments: string[] = [], extra: RequestInit = {}, _apiBase: URI = this.apiBase): Promise { const apiBase = BaseNetwork.urlExtend(_apiBase, segments); - extra = Object.assign({}, extra); - extra.headers = Object.assign( - { Accept: 'application/json' }, - extra.headers); + extra = { + ...extra, + headers: { + Accept: 'application/json', + ...extra.headers, + }, + }; return new Promise((resolve, reject) => { fetch(apiBase.toString(), extra).then((response) => { @@ -382,7 +402,7 @@ export class Authenticator extends BaseNetwork { body: form, }; - this.newCall([], extra).then((json: {token: string}) => { + this.newCall<{token: string}>([], extra).then((json) => { resolve(json.token); }).catch((error: Error) => { reject(error); @@ -400,15 +420,15 @@ export class BaseManager extends BaseNetwork { this.apiBase = BaseNetwork.urlExtend(this.apiBase, ['api', 'v1'].concat(segments)); } - // FIXME: Get the correct type for extra - public newCall(segments: string[] = [], extra: any = {}, apiBase: any = this.apiBase): Promise { - extra = Object.assign({}, extra); - extra.headers = Object.assign( - { + public newCall(segments: string[] = [], extra: RequestInit = {}, apiBase: any = this.apiBase): Promise { + extra = { + ...extra, + headers: { 'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Token ' + this.credentials.authToken, + ...extra.headers, }, - extra.headers); + }; return super.newCall(segments, extra, apiBase); } @@ -421,8 +441,8 @@ export class JournalManager extends BaseManager { public fetch(journalUid: string): Promise { return new Promise((resolve, reject) => { - this.newCall([journalUid, '']).then((json: JournalJson) => { - const journal = new Journal(json.version); + this.newCall([journalUid, '']).then((json) => { + const journal = new Journal({ uid: json.uid }, json.version); journal.deserialize(json); resolve(journal); }).catch((error: Error) => { @@ -433,9 +453,9 @@ export class JournalManager extends BaseManager { public list(): Promise { return new Promise((resolve, reject) => { - this.newCall().then((json: JournalJson[]) => { + this.newCall().then((json) => { resolve(json.map((val: JournalJson) => { - const journal = new Journal(val.version); + const journal = new Journal({ uid: val.uid }, val.version); journal.deserialize(val); return journal; })); @@ -451,7 +471,7 @@ export class JournalManager extends BaseManager { body: JSON.stringify(journal.serialize()), }; - return this.newCall([], extra); + return this.newCall([], extra); } public update(journal: Journal): Promise<{}> { @@ -460,7 +480,7 @@ export class JournalManager extends BaseManager { body: JSON.stringify(journal.serialize()), }; - return this.newCall([journal.uid, ''], extra); + return this.newCall([journal.uid, ''], extra); } public delete(journal: Journal): Promise<{}> { @@ -485,8 +505,8 @@ export class EntryManager extends BaseManager { }); return new Promise((resolve, reject) => { - this.newCall(undefined, undefined, apiBase).then((json: Array<{}>) => { - resolve(json.map((val: any) => { + this.newCall(undefined, undefined, apiBase).then((json) => { + resolve(json.map((val) => { const entry = new Entry(); entry.deserialize(val); return entry; @@ -524,8 +544,8 @@ export class JournalMembersManager extends BaseManager { public list(): Promise { return new Promise((resolve, reject) => { - this.newCall().then((json: JournalMemberJson[]) => { - resolve(json.map((val: JournalMemberJson) => { + this.newCall().then((json) => { + resolve(json.map((val) => { return val; })); }).catch((error: Error) => { @@ -559,7 +579,7 @@ export class UserInfoManager extends BaseManager { public fetch(owner: string): Promise { return new Promise((resolve, reject) => { - this.newCall([owner, '']).then((json: UserInfoJson) => { + this.newCall([owner, '']).then((json) => { const userInfo = new UserInfo(owner, json.version); userInfo.deserialize(json); resolve(userInfo); diff --git a/src/store/construct.ts b/src/store/construct.ts index dde55ae..56cc5c4 100644 --- a/src/store/construct.ts +++ b/src/store/construct.ts @@ -56,7 +56,7 @@ const journalsDeserialize = (state: {}) => { const newState = new Map(); Object.keys(state).forEach((uid) => { const x = state[uid]; - const ret = new EteSync.Journal(x.version); + const ret = new EteSync.Journal(uid, x.version); ret.deserialize(x); newState.set(uid, ret); });