Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updates related to #1509 #5330

Draft
wants to merge 39 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5520fe0
use chrome.runtime broadcast sends with custom generated tabId
Jul 26, 2023
2a252a6
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Jul 28, 2023
4c1ab6a
postMessage as backup (wip)
Jul 28, 2023
f76d041
simplify, removed background relay (wip)
Jul 29, 2023
df8cf42
fix broadcast propagation
Jul 30, 2023
3cfce61
fix parent propagation
Jul 31, 2023
640c60b
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Jul 31, 2023
3c57a7a
fetch() instead of BrowserMsg.ajax (wip)
Aug 7, 2023
eb7fed8
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Aug 7, 2023
7405ae3
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Aug 8, 2023
ada3139
better custom reponseText handling for 400 Bad request and test robus…
Aug 8, 2023
1f73b14
proper network error handling
Aug 10, 2023
cc22d8b
Update mock for token substitution
Aug 10, 2023
b4d4fbc
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Aug 16, 2023
5a55e51
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Aug 16, 2023
bab1c82
download progress and timeout implementation with fetch()
Aug 17, 2023
7a75e96
convert 'Failed to fetch' error to AjaxErr
Aug 17, 2023
9a613c5
Merge remote-tracking branch 'origin/master' into issue-5329-broadcas…
Aug 18, 2023
6769139
HTTP/2 support in mock
Aug 18, 2023
cc3e0b0
Status texts from dictionary for HTTP/2
Aug 18, 2023
058849c
fix HTTP/2 Authority check in mock
Aug 19, 2023
bacb346
safer message handling
Aug 19, 2023
cf079e3
trying to fix decryption
Aug 19, 2023
8902ef6
trying to fix decryption (verbosity)
Aug 19, 2023
c0165f5
trying to fix decryption (moved some code out of async)
Aug 20, 2023
3c506d6
send messages in a safer order
Aug 20, 2023
9924b21
don't send passphrase_entry to self
Aug 20, 2023
da31ca1
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Nov 15, 2023
096e437
fix background ajax
sosnovsky Nov 15, 2023
192be2e
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Nov 29, 2023
fd01bf8
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Dec 14, 2023
685f178
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Dec 18, 2023
27f5e2e
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Dec 19, 2023
ea9dbcb
wip
sosnovsky Dec 19, 2023
154006b
wip
sosnovsky Dec 19, 2023
1aa3fc1
wip
sosnovsky Dec 20, 2023
0049ead
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Jan 31, 2024
21ffb95
fix tests
sosnovsky Feb 1, 2024
ebbb987
Merge branch 'master' into issue-5329-broadcast-live-test
sosnovsky Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions extension/chrome/elements/passphrase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@ View.run(
};

private closeDialog = (entered = false, initiatorFrameId?: string) => {
BrowserMsg.send.closeDialog(this);
BrowserMsg.send.passphraseEntry({ entered, initiatorFrameId });
BrowserMsg.send.passphraseEntry({ entered, initiatorFrameId }, () => BrowserMsg.send.closeDialog(this));
};

private submitHandler = async () => {
Expand Down
21 changes: 3 additions & 18 deletions extension/chrome/elements/pgp_block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

'use strict';

import { Url, Value } from '../../js/common/core/common.js';
import { Url } from '../../js/common/core/common.js';
import { Assert } from '../../js/common/assert.js';
import { RenderMessage } from '../../js/common/render-message.js';
import { Attachment } from '../../js/common/core/attachment.js';
Expand All @@ -14,7 +14,7 @@ import { PgpBlockViewQuoteModule } from './pgp_block_modules/pgp-block-quote-mod
import { PgpBlockViewRenderModule } from './pgp_block_modules/pgp-block-render-module.js';
import { CommonHandlers, Ui } from '../../js/common/browser/ui.js';
import { View } from '../../js/common/view.js';
import { Bm, BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { BrowserMsg } from '../../js/common/browser/browser-msg.js';

export class PgpBlockView extends View {
public readonly acctEmail: string; // needed for attachment decryption, probably should be refactored out
Expand All @@ -28,7 +28,6 @@ export class PgpBlockView extends View {
public readonly renderModule: PgpBlockViewRenderModule;
public readonly printModule = new PgpBlockViewPrintModule();
private readonly tabId = BrowserMsg.generateTabId();

private progressOperation?: {
text: string;
operationId: string; // to ignore possible stray notifications, we generate an id for each operation
Expand Down Expand Up @@ -65,25 +64,11 @@ export class PgpBlockView extends View {
BrowserMsg.addListener('pgp_block_render', async (msg: RenderMessage) => {
this.processMessage(msg);
});
BrowserMsg.addListener('ajax_progress', async (progress: Bm.AjaxProgress) => {
this.handleAjaxProgress(progress);
});
BrowserMsg.addListener('confirmation_result', CommonHandlers.createAsyncResultHandler());
BrowserMsg.listen([this.getDest(), this.frameId]); // we receive non-critical ajax_progress calls via frameId address
BrowserMsg.listen(this.getDest());
BrowserMsg.send.pgpBlockReady(this, { frameId: this.frameId, messageSender: this.getDest() });
};

private handleAjaxProgress = ({ operationId, percent, loaded, total, expectedTransferSize }: Bm.AjaxProgress) => {
if (this.progressOperation && this.progressOperation.operationId === operationId) {
const perc = Value.getPercentage(percent, loaded, total, expectedTransferSize);
if (typeof perc !== 'undefined') {
this.renderProgress({ ...this.progressOperation, perc });
}
return true;
}
return false;
};

private renderProgress = ({ operationId, text, perc, init }: { operationId: string; text: string; perc?: number; init?: boolean }) => {
if (init) {
this.progressOperation = { operationId, text };
Expand Down
13 changes: 10 additions & 3 deletions extension/chrome/settings/setup/setup-key-manager-autogen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { SetupOptions, SetupView } from '../setup.js';
import { Ui } from '../../../js/common/browser/ui.js';
import { Url } from '../../../js/common/core/common.js';
import { AcctStore } from '../../../js/common/platform/store/acct-store.js';
import { ApiErr } from '../../../js/common/api/shared/api-error.js';
import { AjaxErr, ApiErr } from '../../../js/common/api/shared/api-error.js';
import { Api } from '../../../js/common/api/shared/api.js';
import { Settings } from '../../../js/common/settings.js';
import { KeyUtil } from '../../../js/common/core/crypto/key.js';
Expand Down Expand Up @@ -81,9 +81,16 @@ export class SetupWithEmailKeyManagerModule {
} catch (e) {
if (ApiErr.isNetErr(e) && (await Api.isInternetAccessible())) {
// frendly message when key manager is down, helpful during initial infrastructure setup
e.message =
`FlowCrypt Email Key Manager at ${this.view.clientConfiguration.getKeyManagerUrlForPrivateKeys()} cannot be reached. ` +
const url = this.view.clientConfiguration.getKeyManagerUrlForPrivateKeys();
const message =
`FlowCrypt Email Key Manager at ${url} cannot be reached. ` +
'If your organization requires a VPN, please connect to it. Else, please inform your network admin.';
if (e instanceof AjaxErr) {
e.message = message; // we assume isNetErr wasn't identified by the message property
} else {
// convert to AjaxErr, identifiable as a net error, with a custom message
throw AjaxErr.fromNetErr(message, e.stack, url);
}
}
throw e;
}
Expand Down
5 changes: 4 additions & 1 deletion extension/css/cryptup.css
Original file line number Diff line number Diff line change
Expand Up @@ -2833,7 +2833,10 @@ body#new_message.full_window {
}

body#new_message.full_window table#compose {
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
box-shadow:
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12),
0 5px 5px -3px rgba(0, 0, 0, 0.2);
}

div#reply_message_table_container {
Expand Down
1 change: 0 additions & 1 deletion extension/js/background_page/background_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ console.info('background_process.js starting');
// is fixed, this can be moved to the gmail content script, and some may be removed
BrowserMsg.addPgpListeners();

BrowserMsg.bgAddListener('ajax', BgHandlers.ajaxHandler);
BrowserMsg.bgAddListener('ajaxGmailAttachmentGetChunk', BgHandlers.ajaxGmailAttachmentGetChunkHandler);
BrowserMsg.bgAddListener('settings', BgHandlers.openSettingsPageHandler);
BrowserMsg.bgAddListener('update_uninstall_url', BgHandlers.updateUninstallUrl);
Expand Down
16 changes: 1 addition & 15 deletions extension/js/background_page/bg-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

'use strict';

import { Api } from '../common/api/shared/api.js';
import { BgUtils } from './bgutils.js';
import { Bm, BrowserMsg } from '../common/browser/browser-msg.js';
import { Bm } from '../common/browser/browser-msg.js';
import { Gmail } from '../common/api/email-provider/gmail/gmail.js';
import { GlobalStore } from '../common/platform/store/global-store.js';
import { ContactStore } from '../common/platform/store/contact-store.js';
Expand All @@ -29,19 +28,6 @@ export class BgHandlers {
return await dbFunc(db, ...request.args);
};

public static ajaxHandler = async (r: Bm.Ajax): Promise<Bm.Res.Ajax> => {
if (r.req.context?.operationId) {
// progress updates were requested via messages
const frameId = r.req.context.frameId;
const operationId = r.req.context.operationId;
const expectedTransferSize = r.req.context.expectedTransferSize;
r.req.xhr = Api.getAjaxProgressXhrFactory({
download: (percent, loaded, total) => BrowserMsg.send.ajaxProgress(frameId, { percent, loaded, total, expectedTransferSize, operationId }),
});
}
return await Api.ajax(r.req, r.stack);
};

public static ajaxGmailAttachmentGetChunkHandler = async (r: Bm.AjaxGmailAttachmentGetChunk): Promise<Bm.Res.AjaxGmailAttachmentGetChunk> => {
return { chunk: await new Gmail(r.acctEmail).attachmentGetChunk(r.msgId, r.attachmentId) };
};
Expand Down
97 changes: 47 additions & 50 deletions extension/js/common/api/account-servers/external-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact [email protected] */
'use strict';

import { Api, ProgressCb, ProgressCbs, ReqFmt, ReqMethod } from '../shared/api.js';
import { Api, ProgressCb, ProgressCbs } from '../shared/api.js';
import { AcctStore } from '../../platform/store/acct-store.js';
import { Dict, Str } from '../../core/common.js';
import { ErrorReport } from '../../platform/catch.js';
Expand All @@ -12,6 +12,7 @@ import { ParsedRecipients } from '../email-provider/email-provider-api.js';
import { Buf } from '../../core/buf.js';
import { ClientConfigurationError, ClientConfigurationJson } from '../../client-configuration.js';
import { InMemoryStore } from '../../platform/store/in-memory-store.js';
import { Serializable } from '../../platform/store/abstract-store.js';
import { GoogleOAuth } from '../authentication/google/google-oauth.js';

// todo - decide which tags to use
Expand Down Expand Up @@ -84,11 +85,11 @@ export class ExternalService extends Api {
};

public getServiceInfo = async (): Promise<FesRes.ServiceInfo> => {
return await this.request<FesRes.ServiceInfo>('GET', `/api/`);
return await this.request<FesRes.ServiceInfo>(`/api/`);
};

public fetchAndSaveClientConfiguration = async (): Promise<ClientConfigurationJson> => {
const r = await this.request<FesRes.ClientConfiguration>('GET', `/api/${this.apiVersion}/client-configuration?domain=${this.domain}`);
const r = await this.request<FesRes.ClientConfiguration>(`/api/${this.apiVersion}/client-configuration?domain=${this.domain}`);
if (r.clientConfiguration && !r.clientConfiguration.flags) {
throw new ClientConfigurationError('missing_flags');
}
Expand All @@ -97,28 +98,26 @@ export class ExternalService extends Api {
};

public reportException = async (errorReport: ErrorReport): Promise<void> => {
await this.request<void>('POST', `/api/${this.apiVersion}/log-collector/exception`, {}, errorReport);
await this.request<void>(`/api/${this.apiVersion}/log-collector/exception`, { fmt: 'JSON', data: errorReport });
};

public helpFeedback = async (email: string, message: string): Promise<FesRes.HelpFeedback> => {
return await this.request<FesRes.HelpFeedback>('POST', `/api/${this.apiVersion}/account/feedback`, {}, { email, message });
return await this.request<FesRes.HelpFeedback>(`/api/${this.apiVersion}/account/feedback`, { fmt: 'JSON', data: { email, message } });
};

public reportEvent = async (tags: EventTag[], message: string, details?: string): Promise<void> => {
await this.request<void>(
'POST',
`/api/${this.apiVersion}/log-collector/exception`,
{},
{
await this.request<void>(`/api/${this.apiVersion}/log-collector/exception`, {
fmt: 'JSON',
data: {
tags,
message,
details,
}
);
},
});
};

public webPortalMessageNewReplyToken = async (): Promise<FesRes.ReplyToken> => {
return await this.request<FesRes.ReplyToken>('POST', `/api/${this.apiVersion}/message/new-reply-token`, {}, {});
return await this.request<FesRes.ReplyToken>(`/api/${this.apiVersion}/message/new-reply-token`, { fmt: 'JSON', data: {} });
};

public webPortalMessageUpload = async (
Expand Down Expand Up @@ -147,50 +146,52 @@ export class ExternalService extends Api {
),
});
const multipartBody = { content, details };
return await this.request<FesRes.MessageUpload>('POST', `/api/${this.apiVersion}/message`, {}, multipartBody, { upload: progressCb });
return await this.request<FesRes.MessageUpload>(`/api/${this.apiVersion}/message`, { fmt: 'FORM', data: multipartBody }, { upload: progressCb });
};

public messageGatewayUpdate = async (externalId: string, emailGatewayMessageId: string) => {
await this.request<void>(
'POST',
`/api/${this.apiVersion}/message/${externalId}/gateway`,
{},
{
await this.request<void>(`/api/${this.apiVersion}/message/${externalId}/gateway`, {
fmt: 'JSON',
data: {
emailGatewayMessageId,
}
);
},
});
};

private authHdr = async (): Promise<Dict<string>> => {
private authHdr = async (): Promise<{ authorization: string }> => {
const idToken = await InMemoryStore.getUntilAvailable(this.acctEmail, InMemoryStoreKeys.ID_TOKEN);
if (idToken) {
return { Authorization: `Bearer ${idToken}` }; // eslint-disable-line @typescript-eslint/naming-convention
return { authorization: `Bearer ${idToken}` };
}
// user will not actually see this message, they'll see a generic login prompt
throw new BackendAuthErr('Missing id token, please re-authenticate');
};

private request = async <RT>(method: ReqMethod, path: string, headers: Dict<string> = {}, vals?: Dict<unknown>, progress?: ProgressCbs): Promise<RT> => {
let reqFmt: ReqFmt | undefined;
if (progress) {
reqFmt = 'FORM';
} else if (method !== 'GET') {
reqFmt = 'JSON';
}
private request = async <RT>(
path: string,
vals?:
| {
data: Dict<string | Attachment>;
fmt: 'FORM';
}
| { data: Dict<Serializable>; fmt: 'JSON' },
progress?: ProgressCbs
): Promise<RT> => {
const values:
| {
data: Dict<string | Attachment>;
fmt: 'FORM';
method: 'POST';
}
| { data: Dict<Serializable>; fmt: 'JSON'; method: 'POST' }
| undefined = vals
? {
...vals,
method: 'POST',
}
: undefined;
try {
return await ExternalService.apiCall(
this.url,
path,
vals,
reqFmt,
progress,
{
...headers,
...(await this.authHdr()),
},
'json',
method
);
return await ExternalService.apiCall(this.url, path, values, progress, await this.authHdr(), 'json');
} catch (firstAttemptErr) {
const idToken = await InMemoryStore.get(this.acctEmail, InMemoryStoreKeys.ID_TOKEN);
if (ApiErr.isAuthErr(firstAttemptErr) && idToken) {
Expand All @@ -200,16 +201,12 @@ export class ExternalService extends Api {
return await ExternalService.apiCall(
this.url,
path,
vals,
reqFmt,
values,
progress,
{
...headers,
// eslint-disable-next-line @typescript-eslint/naming-convention
Authorization: await GoogleOAuth.googleApiAuthHeader(email, true),
authorization: await GoogleOAuth.googleApiAuthHeader(email, true),
},
'json',
method
'json'
);
}
}
Expand Down
Loading