diff --git a/proxy/nativemessage.ts b/proxy/nativemessage.ts
index 911fe904..0af48d98 100644
--- a/proxy/nativemessage.ts
+++ b/proxy/nativemessage.ts
@@ -1,7 +1,7 @@
/* tslint:disable:no-console */
import IPC from 'ipc';
-// Mostly copied from the example on
+// Mostly based on the example from MDN,
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging
export default class NativeMessage {
ipc: IPC;
diff --git a/src/app/accounts/settings.component.html b/src/app/accounts/settings.component.html
index f54b90a2..ffc57907 100644
--- a/src/app/accounts/settings.component.html
+++ b/src/app/accounts/settings.component.html
@@ -88,6 +88,16 @@
{{'disableFaviconDesc' | i18n}}
+
diff --git a/src/app/accounts/settings.component.ts b/src/app/accounts/settings.component.ts
index fafa8177..9064b372 100644
--- a/src/app/accounts/settings.component.ts
+++ b/src/app/accounts/settings.component.ts
@@ -33,6 +33,7 @@ export class SettingsComponent implements OnInit {
vaultTimeoutAction: string;
pin: boolean = null;
disableFavicons: boolean = false;
+ enableBrowserIntegration: boolean = false;
enableMinToTray: boolean = false;
enableCloseToTray: boolean = false;
enableTray: boolean = false;
@@ -119,6 +120,7 @@ export class SettingsComponent implements OnInit {
const pinSet = await this.vaultTimeoutService.isPinLockSet();
this.pin = pinSet[0] || pinSet[1];
this.disableFavicons = await this.storageService.get(ConstantsService.disableFaviconKey);
+ this.enableBrowserIntegration = await this.storageService.get(ElectronConstants.enableBrowserIntegration);
this.enableMinToTray = await this.storageService.get(ElectronConstants.enableMinimizeToTrayKey);
this.enableCloseToTray = await this.storageService.get(ElectronConstants.enableCloseToTrayKey);
this.enableTray = await this.storageService.get(ElectronConstants.enableTrayKey);
@@ -277,6 +279,11 @@ export class SettingsComponent implements OnInit {
});
}
+ async saveBrowserIntegration() {
+ await this.storageService.save(ElectronConstants.enableBrowserIntegration, this.enableBrowserIntegration);
+ this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration');
+ }
+
private callAnalytics(name: string, enabled: boolean) {
const status = enabled ? 'Enabled' : 'Disabled';
this.analytics.eventTrack.next({ action: `${status} ${name}` });
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index 96f83d64..393896c9 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -1401,5 +1401,11 @@
},
"masterPasswordPolicyRequirementsNotMet": {
"message": "Your new master password does not meet the policy requirements."
+ },
+ "enableBrowserIntegration": {
+ "message": "Enable browser integration"
+ },
+ "enableBrowserIntegrationDesc": {
+ "message": ""
}
}
diff --git a/src/main.ts b/src/main.ts
index ede80322..bc94286e 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -144,6 +144,10 @@ export class Main {
await this.biometricMain.init();
}
+ if (await this.storageService.get(ElectronConstants.enableBrowserIntegration)) {
+ this.nativeMessagingService.listen();
+ }
+
if (!app.isDefaultProtocolClient('bitwarden')) {
app.setAsDefaultProtocolClient('bitwarden');
}
@@ -157,8 +161,6 @@ export class Main {
// tslint:disable-next-line
console.error(e);
});
- this.nativeMessagingService.listen();
- this.nativeMessagingService.generateManifests();
}
private processDeepLink(argv: string[]): void {
diff --git a/src/main/messaging.main.ts b/src/main/messaging.main.ts
index 1e3af95e..f5acb3d0 100644
--- a/src/main/messaging.main.ts
+++ b/src/main/messaging.main.ts
@@ -44,6 +44,14 @@ export class MessagingMain {
case 'hideToTray':
this.main.trayMain.hideToTray();
break;
+ case 'enableBrowserIntegration':
+ this.main.nativeMessagingService.generateManifests();
+ this.main.nativeMessagingService.listen();
+ break;
+ case 'disableBrowserIntegration':
+ this.main.nativeMessagingService.removeManifests();
+ this.main.nativeMessagingService.stop();
+ break;
default:
break;
}
diff --git a/src/services/nativeMessaging.service.ts b/src/services/nativeMessaging.service.ts
index 1b9c2575..97080e43 100644
--- a/src/services/nativeMessaging.service.ts
+++ b/src/services/nativeMessaging.service.ts
@@ -38,6 +38,10 @@ export class NativeMessagingService {
ipc.server.start();
}
+ stop() {
+ ipc.server.stop();
+ }
+
generateManifests() {
const baseJson = {
'name': 'com.8bit.bitwarden',
@@ -90,6 +94,35 @@ export class NativeMessagingService {
}
}
+ removeManifests() {
+ switch (process.platform) {
+ case 'win32':
+ this.deleteWindowsRegistry('HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden');
+ this.deleteWindowsRegistry('HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden');
+ break;
+ case 'darwin':
+ if (existsSync('~/Library/Application Support/Mozilla/NativeMessagingHosts/com.8bit.bitwarden.json')) {
+ fs.unlink('~/Library/Application Support/Mozilla/NativeMessagingHosts/com.8bit.bitwarden.json')
+ }
+
+ if (existsSync('~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.8bit.bitwarden.json')) {
+ fs.unlink('~/Library/Application Support/Mozilla/NativeMessagingHosts/com.8bit.bitwarden.json')
+ }
+ break;
+ case 'linux':
+ if (existsSync('~/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json')) {
+ fs.unlink('~/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json')
+ }
+
+ if (existsSync('~/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json')) {
+ fs.unlink('~/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json')
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
private writeManifest(destination: string, manifest: object) {
fs.writeFile(destination, JSON.stringify(manifest, null, 2)).catch(this.logService.error);
}
@@ -106,7 +139,7 @@ export class NativeMessagingService {
}
}
- private createWindowsRegistry(check: string, location: string, jsonFile: string) {
+ private async createWindowsRegistry(check: string, location: string, jsonFile: string) {
const regedit = require('regedit');
regedit.setExternalVBSLocation('resources/regedit/vbs');
@@ -117,23 +150,43 @@ export class NativeMessagingService {
this.logService.debug(`Adding registry: ${location}`)
// Check installed
- list(check)
- .then(() => {
- // Create path
- return createKey(location);
- })
- .then(() => {
- // Insert path to manifest
- const obj: any = {};
- obj[location] = {
- 'default': {
- value: path.join(this.userPath, 'browsers', jsonFile),
- type: 'REG_DEFAULT',
- },
- }
+ try {
+ await list(check)
+ } catch {
+ return;
+ }
- return putValue(obj);
- })
- .catch(this.logService.error)
+ try {
+ await createKey(location);
+
+ // Insert path to manifest
+ const obj: any = {};
+ obj[location] = {
+ 'default': {
+ value: path.join(this.userPath, 'browsers', jsonFile),
+ type: 'REG_DEFAULT',
+ },
+ }
+
+ return putValue(obj);
+ } catch (error) {
+ this.logService.error(error);
+ }
+ }
+
+ private async deleteWindowsRegistry(key: string) {
+ const regedit = require("regedit");
+
+ const list = util.promisify(regedit.list);
+ const deleteKey = util.promisify(regedit.deleteKey);
+
+ this.logService.debug(`Removing registry: ${location}`)
+
+ try {
+ await list(key);
+ await deleteKey(key);
+ } catch {
+ // Do nothing
+ }
}
}