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

[PM-3905] Address PR feedback v2 #6322

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3c651be
[PM-3905] chore: move webauthn utils to vault
coroiu Sep 18, 2023
41cba00
[PM-3905] chore: make static function private
coroiu Sep 18, 2023
b502d88
[PM-3905] chore: add documentation to user interface classes
coroiu Sep 18, 2023
44b3753
[PM-3905] chore: clean up unused abort controllers
coroiu Sep 18, 2023
8e2bb9d
[PM-3905] chore: add documentation to fido2 client and authenticatio
coroiu Sep 18, 2023
b71513a
[PM-3905] chore: extract create credential params mapping to separate…
coroiu Sep 18, 2023
5d275da
[PM-3905] chore: extract get assertion params mapping to separate fun…
coroiu Sep 18, 2023
565e050
[PM-3905] chore: assign requireResidentKey as separate variable
coroiu Sep 18, 2023
1944d02
[PM-3905] feat: started rewrite of messenger
coroiu Sep 20, 2023
6383347
[PM-3905] feat: complete rewrite of messenger
coroiu Sep 20, 2023
ef24a5a
[PM-3905] chore: clarify why we're assigning to window
coroiu Sep 20, 2023
a7eef67
[PM-3905] feat: clean up tests
coroiu Sep 20, 2023
ea7cb18
[PM-3905] docs: document messenger class
coroiu Sep 20, 2023
a1e6d16
[PM-3905] feat: remove `requestId` which is no longer needed
coroiu Sep 20, 2023
3b34b24
[PM-3905] feat: simplify message structure
coroiu Sep 20, 2023
da03278
[PM-3905] chore: typo
coroiu Sep 20, 2023
b710fe0
[PM-3905] chore: clean up old file
coroiu Sep 20, 2023
be077ea
[PM-3905] chore: tweak doc comment
coroiu Sep 20, 2023
e4e016e
[PM-3905] feat: create separate class for managing aborts
coroiu Sep 20, 2023
5d6968a
[PM-3905] chore: move abort manager to vault
coroiu Sep 21, 2023
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
23 changes: 9 additions & 14 deletions apps/browser/src/background/runtime.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { BrowserPopoutWindowService } from "../platform/popup/abstractions/browser-popout-window.service";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service";
import { AbortManager } from "../vault/background/abort-manager";

import MainBackground from "./main.background";
import LockedVaultPendingNotificationsItem from "./models/lockedVaultPendingNotificationsItem";
Expand All @@ -21,7 +22,7 @@
private pageDetailsToAutoFill: any[] = [];
private onInstalledReason: string = null;
private lockedVaultPendingNotifications: LockedVaultPendingNotificationsItem[] = [];
private abortControllers = new Map<string, AbortController>();
private abortManager = new AbortManager();

constructor(
private main: MainBackground,
Expand Down Expand Up @@ -253,18 +254,18 @@
this.platformUtilsService.copyToClipboard(msg.identifier, { window: window });
break;
case "fido2AbortRequest":
this.abortControllers.get(msg.abortedRequestId)?.abort();
this.abortManager.abort(msg.abortedRequestId);
break;
case "checkFido2FeatureEnabled":
return await this.main.fido2ClientService.isFido2FeatureEnabled();
case "fido2RegisterCredentialRequest":
return await this.main.fido2ClientService
.createCredential(msg.data, this.createAbortController(msg.requestId))
.finally(() => this.abortControllers.delete(msg.requestId));
return await this.abortManager.runWithAbortController(msg.requestId, (abortController) =>
this.main.fido2ClientService.createCredential(msg.data, abortController)
);
case "fido2GetCredentialRequest":
return await this.main.fido2ClientService
.assertCredential(msg.data, this.createAbortController(msg.requestId))
.finally(() => this.abortControllers.delete(msg.requestId));
return await this.abortManager.runWithAbortController(msg.requestId, (abortController) =>
this.main.fido2ClientService.assertCredential(msg.data, abortController)
);

Check notice on line 268 in apps/browser/src/background/runtime.background.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (EC-598-beeep-properly-store-passkeys-in-bitwarden)

✅ Getting better: Complex Method

RuntimeBackground.processMessage decreases in cyclomatic complexity from 48 to 47, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
}
}

Expand Down Expand Up @@ -301,10 +302,4 @@
}
}, 100);
}

private createAbortController(id: string): AbortController {
const abortController = new AbortController();
this.abortControllers.set(id, abortController);
return abortController;
}
}
21 changes: 21 additions & 0 deletions apps/browser/src/vault/background/abort-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
type Runner<T> = (abortController: AbortController) => Promise<T>;

/**
* Manages abort controllers for long running tasks and allow separate
* execution contexts to abort each other by using ids.
*/
export class AbortManager {
private abortControllers = new Map<string, AbortController>();

runWithAbortController<T>(id: string, runner: Runner<T>): Promise<T> {
const abortController = new AbortController();
this.abortControllers.set(id, abortController);
return runner(abortController).finally(() => {
this.abortControllers.delete(id);
});
}

abort(id: string) {
this.abortControllers.get(id)?.abort();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type BrowserFido2Message = { sessionId: string } & (
}
/**
* This message is used to announce the creation of a new session.
* It iss used by popouts to know when to close.
* It is used by popouts to know when to close.
**/
| {
type: "NewSessionCreatedRequest";
Expand Down Expand Up @@ -89,6 +89,10 @@ export type BrowserFido2Message = { sessionId: string } & (
}
);

/**
* Browser implementation of the {@link Fido2UserInterfaceService}.
* The user interface is implemented as a popout and the service uses the browser's messaging API to communicate with it.
*/
export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
constructor(private popupUtilsService: PopupUtilsService) {}

Expand Down Expand Up @@ -188,12 +192,6 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi
});
}

fallbackRequested = false;

get aborted() {
return this.abortController.signal.aborted;
}

async pickCredential({
cipherIds,
userVerification,
Expand Down
7 changes: 4 additions & 3 deletions apps/browser/src/vault/fido2/content/content-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ function initializeFido2ContentScript(isFido2FeatureEnabled: boolean) {
const messenger = Messenger.forDOMCommunication(window);

messenger.handler = async (message, abortController) => {
const requestId = Date.now().toString();
const abortHandler = () =>
chrome.runtime.sendMessage({
command: "fido2AbortRequest",
abortedRequestId: message.metadata.requestId,
abortedRequestId: requestId,
});
abortController.signal.addEventListener("abort", abortHandler);

Expand All @@ -33,7 +34,7 @@ function initializeFido2ContentScript(isFido2FeatureEnabled: boolean) {
{
command: "fido2RegisterCredentialRequest",
data: message.data,
requestId: message.metadata.requestId,
requestId: requestId,
},
(response) => {
if (response.error !== undefined) {
Expand All @@ -55,7 +56,7 @@ function initializeFido2ContentScript(isFido2FeatureEnabled: boolean) {
{
command: "fido2GetCredentialRequest",
data: message.data,
requestId: message.metadata.requestId,
requestId: requestId,
},
(response) => {
if (response.error !== undefined) {
Expand Down
43 changes: 33 additions & 10 deletions apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Subject } from "rxjs";

import { Utils } from "@bitwarden/common/platform/misc/utils";

import { Message } from "./message";
Expand All @@ -12,6 +10,9 @@ describe("Messenger", () => {
let handlerB: TestMessageHandler;

beforeEach(() => {
// jest does not support MessageChannel
window.MessageChannel = MockMessageChannel as any;

const channelPair = new TestChannelPair();
messengerA = new Messenger(channelPair.channelA);
messengerB = new Messenger(channelPair.channelB);
Expand Down Expand Up @@ -81,19 +82,16 @@ class TestChannelPair {
readonly channelB: Channel;

constructor() {
const subjectA = new Subject<MessageWithMetadata>();
const subjectB = new Subject<MessageWithMetadata>();
const broadcastChannel = new MockMessageChannel<MessageWithMetadata>();

this.channelA = {
messages$: subjectA,
postMessage: (message) => {
subjectB.next(message);
},
addEventListener: (listener) => (broadcastChannel.port1.onmessage = listener),
postMessage: (message, port) => broadcastChannel.port1.postMessage(message, port),
};

this.channelB = {
messages$: subjectB,
postMessage: (message) => subjectA.next(message),
addEventListener: (listener) => (broadcastChannel.port2.onmessage = listener),
postMessage: (message, port) => broadcastChannel.port2.postMessage(message, port),
};
}
}
Expand Down Expand Up @@ -129,3 +127,28 @@ class TestMessageHandler {
return received;
}
}

class MockMessageChannel<T> {
port1 = new MockMessagePort<T>();
port2 = new MockMessagePort<T>();

constructor() {
this.port1.remotePort = this.port2;
this.port2.remotePort = this.port1;
}
}

class MockMessagePort<T> {
onmessage: ((ev: MessageEvent<T>) => any) | null;
remotePort: MockMessagePort<T>;

postMessage(message: T, port?: MessagePort) {
this.remotePort.onmessage(
new MessageEvent("message", { data: message, ports: port ? [port] : [] })
);
}

close() {
// Do nothing
}
}
Loading
Loading