Skip to content

Commit

Permalink
Merge branch 'main' into pm-8161-payment-optional-trial-mvp
Browse files Browse the repository at this point in the history
  • Loading branch information
cyprain-okeke authored Nov 1, 2024
2 parents b368203 + a049b55 commit dcb9178
Show file tree
Hide file tree
Showing 25 changed files with 248 additions and 68 deletions.
1 change: 1 addition & 0 deletions apps/browser/src/background/main.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,7 @@ export default class MainBackground {
this.stateService,
this.keyGenerationService,
this.encryptService,
this.logService,
);

this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/auth/commands/unlock.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class UnlockCommand {
return Response.error(e.message);
}

const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);
await this.keyService.setUserKey(userKey, userId);

if (await this.keyConnectorService.getConvertAccountRequired()) {
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/service-container/service-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ export class ServiceContainer {
this.stateService,
this.keyGenerationService,
this.encryptService,
this.logService,
);

this.kdfConfigService = new KdfConfigService(this.stateProvider);
Expand Down
9 changes: 8 additions & 1 deletion apps/desktop/desktop_native/core/src/ipc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub fn path(name: &str) -> std::path::PathBuf {
format!(r"\\.\pipe\{hash_b64}.app.{name}").into()
}

#[cfg(target_os = "macos")]
#[cfg(all(target_os = "macos", not(debug_assertions)))]
{
let mut home = dirs::home_dir().unwrap();

Expand All @@ -53,6 +53,13 @@ pub fn path(name: &str) -> std::path::PathBuf {
tmp.join(format!("app.{name}"))
}

#[cfg(all(target_os = "macos", debug_assertions))]
{
// When running in debug mode, we use the tmp dir because the app is not sandboxed
let dir = std::env::temp_dir();
dir.join(format!("app.{name}"))
}

#[cfg(target_os = "linux")]
{
// On Linux, we use the user's cache directory.
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src/app/accounts/settings.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
async saveBrowserIntegration() {
if (
ipc.platform.deviceType === DeviceType.MacOsDesktop &&
!this.platformUtilsService.isMacAppStore()
!this.platformUtilsService.isMacAppStore() &&
!ipc.platform.isDev
) {
await this.dialogService.openSimpleDialog({
title: { key: "browserIntegrationUnsupportedTitle" },
Expand Down
165 changes: 117 additions & 48 deletions apps/desktop/src/main/native-messaging.main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,7 @@ export class NativeMessagingMain {
};
const chromeJson = {
...baseJson,
...{
allowed_origins: [
// Chrome extension
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
// Chrome beta extension
"chrome-extension://hccnnhgbibccigepcmlgppchkpfdophk/",
// Edge extension
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
// Opera extension
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
],
},
allowed_origins: await this.loadChromeIds(),
};

switch (process.platform) {
Expand Down Expand Up @@ -180,35 +169,26 @@ export class NativeMessagingMain {
}
break;
}
case "linux":
if (existsSync(`${this.homedir()}/.mozilla/`)) {
await this.writeManifest(
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
firefoxJson,
);
}

if (existsSync(`${this.homedir()}/.config/google-chrome/`)) {
await this.writeManifest(
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
chromeJson,
);
}

if (existsSync(`${this.homedir()}/.config/microsoft-edge/`)) {
await this.writeManifest(
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
chromeJson,
);
}

if (existsSync(`${this.homedir()}/.config/chromium/`)) {
await this.writeManifest(
`${this.homedir()}/.config/chromium/NativeMessagingHosts/com.8bit.bitwarden.json`,
chromeJson,
);
case "linux": {
for (const [key, value] of Object.entries(this.getLinuxNMHS())) {
if (existsSync(value)) {
if (key === "Firefox") {
await this.writeManifest(
path.join(value, "native-messaging-hosts", "com.8bit.bitwarden.json"),
firefoxJson,
);
} else {
await this.writeManifest(
path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"),
chromeJson,
);
}
} else {
this.logService.warning(`${key} not found, skipping.`);
}
}
break;
}
default:
break;
}
Expand Down Expand Up @@ -260,15 +240,18 @@ export class NativeMessagingMain {
break;
}
case "linux": {
await this.removeIfExists(
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
);
await this.removeIfExists(
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
);
await this.removeIfExists(
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
);
for (const [key, value] of Object.entries(this.getLinuxNMHS())) {
if (key === "Firefox") {
await this.removeIfExists(
path.join(value, "native-messaging-hosts", "com.8bit.bitwarden.json"),
);
} else {
await this.removeIfExists(
path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"),
);
}
}

break;
}
default:
Expand Down Expand Up @@ -317,6 +300,15 @@ export class NativeMessagingMain {
/* eslint-enable no-useless-escape */
}

private getLinuxNMHS() {
return {
Firefox: `${this.homedir()}/.mozilla/`,
Chrome: `${this.homedir()}/.config/google-chrome/`,
Chromium: `${this.homedir()}/.config/chromium/`,
"Microsoft Edge": `${this.homedir()}/.config/microsoft-edge/`,
};
}

private async writeManifest(destination: string, manifest: object) {
this.logService.debug(`Writing manifest: ${destination}`);

Expand All @@ -327,6 +319,83 @@ export class NativeMessagingMain {
await fs.writeFile(destination, JSON.stringify(manifest, null, 2));
}

private async loadChromeIds(): Promise<string[]> {
const ids: Set<string> = new Set([
// Chrome extension
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
// Chrome beta extension
"chrome-extension://hccnnhgbibccigepcmlgppchkpfdophk/",
// Edge extension
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
// Opera extension
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
]);

if (!isDev()) {
return Array.from(ids);
}

// The dev builds of the extension have a different random ID per user, so to make development easier
// we try to find the extension IDs from the user's Chrome profiles when we're running in dev mode.
let chromePaths: string[];
switch (process.platform) {
case "darwin": {
chromePaths = Object.entries(this.getDarwinNMHS())
.filter(([key]) => key !== "Firefox")
.map(([, value]) => value);
break;
}
case "linux": {
chromePaths = Object.entries(this.getLinuxNMHS())
.filter(([key]) => key !== "Firefox")
.map(([, value]) => value);
break;
}
case "win32": {
// TODO: Add more supported browsers for Windows?
chromePaths = [
path.join(process.env.LOCALAPPDATA, "Microsoft", "Edge", "User Data"),
path.join(process.env.LOCALAPPDATA, "Google", "Chrome", "User Data"),
];
break;
}
}

for (const chromePath of chromePaths) {
try {
// The chrome profile directories are named "Default", "Profile 1", "Profile 2", etc.
const profiles = (await fs.readdir(chromePath)).filter((f) => {
const lower = f.toLowerCase();
return lower == "default" || lower.startsWith("profile ");
});

for (const profile of profiles) {
try {
// Read the profile Preferences file and find the extension commands section
const prefs = JSON.parse(
await fs.readFile(path.join(chromePath, profile, "Preferences"), "utf8"),
);
const commands: Map<string, any> = prefs.extensions.commands;

// If one of the commands is autofill_login or generate_password, we know it's probably the Bitwarden extension
for (const { command_name, extension } of Object.values(commands)) {
if (command_name === "autofill_login" || command_name === "generate_password") {
ids.add(`chrome-extension://${extension}/`);
this.logService.info(`Found extension from ${chromePath}: ${extension}`);
}
}
} catch (e) {
this.logService.info(`Error reading preferences: ${e}`);
}
}
} catch (e) {
// Browser is not installed, we can just skip it
}
}

return Array.from(ids);
}

private binaryPath() {
const ext = process.platform === "win32" ? ".exe" : "";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export class ChangePasswordComponent
HashPurpose.LocalAuthorization,
);

const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);
if (userKey == null) {
this.toastService.showToast({
variant: "error",
Expand Down
1 change: 1 addition & 0 deletions libs/angular/src/auth/components/lock.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ export class LockComponent implements OnInit, OnDestroy {

const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
response.masterKey,
userId,
);
await this.setUserKeyAndContinue(userKey, userId, true);
}
Expand Down
8 changes: 7 additions & 1 deletion libs/angular/src/services/jslib-services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,13 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: InternalMasterPasswordServiceAbstraction,
useClass: MasterPasswordService,
deps: [StateProvider, StateServiceAbstraction, KeyGenerationServiceAbstraction, EncryptService],
deps: [
StateProvider,
StateServiceAbstraction,
KeyGenerationServiceAbstraction,
EncryptService,
LogService,
],
}),
safeProvider({
provide: MasterPasswordServiceAbstraction,
Expand Down
1 change: 1 addition & 0 deletions libs/auth/src/angular/lock/lock.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ export class LockV2Component implements OnInit, OnDestroy {

const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterPasswordVerificationResponse.masterKey,
this.activeAccount.id,
);
await this.setUserKeyAndContinue(userKey, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
private async trySetUserKeyWithMasterKey(userId: UserId): Promise<void> {
const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId));
if (masterKey) {
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterKey,
userId,
);
await this.keyService.setUserKey(userKey, userId);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ export class PasswordLoginStrategy extends LoginStrategy {

const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId));
if (masterKey) {
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterKey,
userId,
);
await this.keyService.setUserKey(userKey, userId);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ describe("SsoLoginStrategy", () => {

expect(masterPasswordService.mock.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
masterKey,
undefined,
userId,
undefined,
);
expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId);
Expand Down Expand Up @@ -552,7 +552,7 @@ describe("SsoLoginStrategy", () => {

expect(masterPasswordService.mock.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
masterKey,
undefined,
userId,
undefined,
);
expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export class SsoLoginStrategy extends LoginStrategy {
return;
}

const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);
await this.keyService.setUserKey(userKey, userId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ describe("UserApiLoginStrategy", () => {

expect(masterPasswordService.mock.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
masterKey,
undefined,
userId,
undefined,
);
expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ export class UserApiLoginStrategy extends LoginStrategy {
if (response.apiUseKeyConnector) {
const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId));
if (masterKey) {
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterKey,
userId,
);
await this.keyService.setUserKey(userKey, userId);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe("AuthRequestService", () => {
);
expect(masterPasswordService.mock.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
mockDecryptedMasterKey,
undefined,
mockUserId,
undefined,
);
expect(keyService.setUserKey).toHaveBeenCalledWith(mockDecryptedUserKey, mockUserId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
);

// Decrypt and set user key in state
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);

// Set masterKey + masterKeyHash in state after decryption (in case decryption fails)
await this.masterPasswordService.setMasterKey(masterKey, userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ export class PinService implements PinServiceAbstraction {

const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
masterKey,
userId,
encUserKey ? new EncString(encUserKey) : undefined,
);

Expand Down
Loading

0 comments on commit dcb9178

Please sign in to comment.