mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
Desktop Autotype use managed object for IPC vault data payload (#17105)
* Desktop Autotype use managed object for IPC vault data payload * claude feedback 1 * check for falsey values * add unit test * remove unecessary async * helper result type * dear claude
This commit is contained in:
@@ -5,6 +5,7 @@ import { LogService } from "@bitwarden/logging";
|
||||
|
||||
import { WindowMain } from "../../main/window.main";
|
||||
import { stringIsNotUndefinedNullAndEmpty } from "../../utils";
|
||||
import { AutotypeVaultData } from "../models/autotype-vault-data";
|
||||
import { AutotypeKeyboardShortcut } from "../models/main-autotype-keyboard-shortcut";
|
||||
|
||||
export class MainDesktopAutotypeService {
|
||||
@@ -47,18 +48,12 @@ export class MainDesktopAutotypeService {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completeAutotypeRequest", (event, data) => {
|
||||
const { response } = data;
|
||||
|
||||
ipcMain.on("autofill.completeAutotypeRequest", (_event, vaultData: AutotypeVaultData) => {
|
||||
if (
|
||||
stringIsNotUndefinedNullAndEmpty(response.username) &&
|
||||
stringIsNotUndefinedNullAndEmpty(response.password)
|
||||
stringIsNotUndefinedNullAndEmpty(vaultData.username) &&
|
||||
stringIsNotUndefinedNullAndEmpty(vaultData.password)
|
||||
) {
|
||||
this.doAutotype(
|
||||
response.username,
|
||||
response.password,
|
||||
this.autotypeKeyboardShortcut.getArrayFormat(),
|
||||
);
|
||||
this.doAutotype(vaultData, this.autotypeKeyboardShortcut.getArrayFormat());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -89,8 +84,9 @@ export class MainDesktopAutotypeService {
|
||||
: this.logService.info("Enabling autotype failed.");
|
||||
}
|
||||
|
||||
private doAutotype(username: string, password: string, keyboardShortcut: string[]) {
|
||||
const inputPattern = username + "\t" + password;
|
||||
private doAutotype(vaultData: AutotypeVaultData, keyboardShortcut: string[]) {
|
||||
const TAB = "\t";
|
||||
const inputPattern = vaultData.username + TAB + vaultData.password;
|
||||
const inputArray = new Array<number>(inputPattern.length);
|
||||
|
||||
for (let i = 0; i < inputPattern.length; i++) {
|
||||
|
||||
8
apps/desktop/src/autofill/models/autotype-vault-data.ts
Normal file
8
apps/desktop/src/autofill/models/autotype-vault-data.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Vault data used in autotype operations.
|
||||
* `username` and `password` are guaranteed to be not null/undefined.
|
||||
*/
|
||||
export interface AutotypeVaultData {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import type { autofill } from "@bitwarden/desktop-napi";
|
||||
import { Command } from "../platform/main/autofill/command";
|
||||
import { RunCommandParams, RunCommandResult } from "../platform/main/autofill/native-autofill.main";
|
||||
|
||||
import { AutotypeVaultData } from "./models/autotype-vault-data";
|
||||
|
||||
export default {
|
||||
runCommand: <C extends Command>(params: RunCommandParams<C>): Promise<RunCommandResult<C>> =>
|
||||
ipcRenderer.invoke("autofill.runCommand", params),
|
||||
@@ -133,10 +135,7 @@ export default {
|
||||
listenAutotypeRequest: (
|
||||
fn: (
|
||||
windowTitle: string,
|
||||
completeCallback: (
|
||||
error: Error | null,
|
||||
response: { username?: string; password?: string },
|
||||
) => void,
|
||||
completeCallback: (error: Error | null, response: AutotypeVaultData | null) => void,
|
||||
) => void,
|
||||
) => {
|
||||
ipcRenderer.on(
|
||||
@@ -149,7 +148,7 @@ export default {
|
||||
) => {
|
||||
const { windowTitle } = data;
|
||||
|
||||
fn(windowTitle, (error, response) => {
|
||||
fn(windowTitle, (error, vaultData) => {
|
||||
if (error) {
|
||||
ipcRenderer.send("autofill.completeError", {
|
||||
windowTitle,
|
||||
@@ -157,11 +156,9 @@ export default {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ipcRenderer.send("autofill.completeAutotypeRequest", {
|
||||
windowTitle,
|
||||
response,
|
||||
});
|
||||
if (vaultData !== null) {
|
||||
ipcRenderer.send("autofill.completeAutotypeRequest", vaultData);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import { getAutotypeVaultData } from "./desktop-autotype.service";
|
||||
|
||||
describe("getAutotypeVaultData", () => {
|
||||
it("should return vault data when cipher has username and password", () => {
|
||||
const cipherView = new CipherView();
|
||||
cipherView.login.username = "foo";
|
||||
cipherView.login.password = "bar";
|
||||
|
||||
const [error, vaultData] = getAutotypeVaultData(cipherView);
|
||||
|
||||
expect(error).toBeNull();
|
||||
expect(vaultData?.username).toEqual("foo");
|
||||
expect(vaultData?.password).toEqual("bar");
|
||||
});
|
||||
|
||||
it("should return error when firstCipher is undefined", () => {
|
||||
const cipherView = undefined;
|
||||
const [error, vaultData] = getAutotypeVaultData(cipherView);
|
||||
|
||||
expect(vaultData).toBeNull();
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.message).toEqual("No matching vault item.");
|
||||
});
|
||||
|
||||
it("should return error when username is undefined", () => {
|
||||
const cipherView = new CipherView();
|
||||
cipherView.login.username = undefined;
|
||||
cipherView.login.password = "bar";
|
||||
|
||||
const [error, vaultData] = getAutotypeVaultData(cipherView);
|
||||
|
||||
expect(vaultData).toBeNull();
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.message).toEqual("Vault item is undefined.");
|
||||
});
|
||||
|
||||
it("should return error when password is undefined", () => {
|
||||
const cipherView = new CipherView();
|
||||
cipherView.login.username = "foo";
|
||||
cipherView.login.password = undefined;
|
||||
|
||||
const [error, vaultData] = getAutotypeVaultData(cipherView);
|
||||
|
||||
expect(vaultData).toBeNull();
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.message).toEqual("Vault item is undefined.");
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { AutotypeVaultData } from "../models/autotype-vault-data";
|
||||
|
||||
import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service";
|
||||
|
||||
export const defaultWindowsAutotypeKeyboardShortcut: string[] = ["Control", "Shift", "B"];
|
||||
@@ -27,6 +29,8 @@ export const AUTOTYPE_ENABLED = new KeyDefinition<boolean | null>(
|
||||
{ deserializer: (b) => b },
|
||||
);
|
||||
|
||||
export type Result<T, E = Error> = [E, null] | [null, T];
|
||||
|
||||
/*
|
||||
Valid windows shortcut keys: Control, Alt, Super, Shift, letters A - Z
|
||||
Valid macOS shortcut keys: Control, Alt, Command, Shift, letters A - Z
|
||||
@@ -63,11 +67,8 @@ export class DesktopAutotypeService {
|
||||
ipc.autofill.listenAutotypeRequest(async (windowTitle, callback) => {
|
||||
const possibleCiphers = await this.matchCiphersToWindowTitle(windowTitle);
|
||||
const firstCipher = possibleCiphers?.at(0);
|
||||
|
||||
return callback(null, {
|
||||
username: firstCipher?.login?.username,
|
||||
password: firstCipher?.login?.password,
|
||||
});
|
||||
const [error, vaultData] = getAutotypeVaultData(firstCipher);
|
||||
callback(error, vaultData);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -176,3 +177,23 @@ export class DesktopAutotypeService {
|
||||
return possibleCiphers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an `AutotypeVaultData` object or an `Error` if the
|
||||
* cipher or vault data within are undefined.
|
||||
*/
|
||||
export function getAutotypeVaultData(
|
||||
cipherView: CipherView | undefined,
|
||||
): Result<AutotypeVaultData> {
|
||||
if (!cipherView) {
|
||||
return [Error("No matching vault item."), null];
|
||||
} else if (cipherView.login.username === undefined || cipherView.login.password === undefined) {
|
||||
return [Error("Vault item is undefined."), null];
|
||||
} else {
|
||||
const vaultData: AutotypeVaultData = {
|
||||
username: cipherView.login.username,
|
||||
password: cipherView.login.password,
|
||||
};
|
||||
return [null, vaultData];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user