bitwarden-estensione-browser/src/background/main.background.ts

566 lines
20 KiB
TypeScript
Raw Normal View History

2018-01-09 20:26:20 +01:00
import { CipherType } from 'jslib/enums';
import {
ApiService,
AppIdService,
2018-01-09 20:26:20 +01:00
CryptoService,
TokenService,
2018-01-09 23:45:17 +01:00
TotpService,
2018-01-09 23:37:40 +01:00
UserService,
2018-01-09 20:26:20 +01:00
UtilsService,
} from 'jslib/services';
import {
ApiService as ApiServiceAbstraction,
AppIdService as AppIdServiceAbstraction,
2018-01-09 20:26:20 +01:00
CryptoService as CryptoServiceAbstraction,
MessagingService as MessagingServiceAbstraction,
PlatformUtilsService as PlatformUtilsServiceAbstraction,
StorageService as StorageServiceAbstraction,
TokenService as TokenServiceAbstraction,
2018-01-09 23:45:17 +01:00
TotpService as TotpServiceAbstraction,
2018-01-09 23:37:40 +01:00
UserService as UserServiceAbstraction,
2018-01-09 20:26:20 +01:00
UtilsService as UtilsServiceAbstraction,
} from 'jslib/abstractions';
2017-12-05 23:04:30 +01:00
2017-12-07 21:36:24 +01:00
import BrowserApi from '../browser/browserApi';
import CommandsBackground from './commands.background';
2017-12-07 22:02:15 +01:00
import ContextMenusBackground from './contextMenus.background';
import IdleBackground from './idle.background';
import RuntimeBackground from './runtime.background';
import TabsBackground from './tabs.background';
import WebRequestBackground from './webRequest.background';
import WindowsBackground from './windows.background';
2017-12-05 23:04:30 +01:00
import AutofillService from '../services/autofill.service';
2018-01-04 22:06:00 +01:00
import BrowserMessagingService from '../services/browserMessaging.service';
2018-01-05 22:38:50 +01:00
import BrowserPlatformUtilsService from '../services/browserPlatformUtils.service';
2018-01-04 22:15:06 +01:00
import BrowserStorageService from '../services/browserStorage.service';
2017-12-05 23:04:30 +01:00
import CipherService from '../services/cipher.service';
import CollectionService from '../services/collection.service';
import ConstantsService from '../services/constants.service';
import ContainerService from '../services/container.service';
2017-12-05 23:04:30 +01:00
import EnvironmentService from '../services/environment.service';
import FolderService from '../services/folder.service';
import i18nService from '../services/i18n.service';
2017-12-05 23:04:30 +01:00
import LockService from '../services/lock.service';
import PasswordGenerationService from '../services/passwordGeneration.service';
import SettingsService from '../services/settings.service';
import SyncService from '../services/sync.service';
2018-01-04 16:51:08 +01:00
2017-12-05 23:04:30 +01:00
export default class MainBackground {
2018-01-09 20:26:20 +01:00
messagingService: MessagingServiceAbstraction;
storageService: StorageServiceAbstraction;
i18nService: any;
2018-01-09 20:26:20 +01:00
platformUtilsService: PlatformUtilsServiceAbstraction;
utilsService: UtilsServiceAbstraction;
2017-12-05 23:04:30 +01:00
constantsService: ConstantsService;
2018-01-09 20:26:20 +01:00
cryptoService: CryptoServiceAbstraction;
tokenService: TokenServiceAbstraction;
appIdService: AppIdServiceAbstraction;
apiService: ApiServiceAbstraction;
2017-12-05 23:04:30 +01:00
environmentService: EnvironmentService;
2018-01-09 23:37:40 +01:00
userService: UserServiceAbstraction;
2017-12-05 23:04:30 +01:00
settingsService: SettingsService;
cipherService: CipherService;
folderService: FolderService;
collectionService: CollectionService;
lockService: LockService;
syncService: SyncService;
passwordGenerationService: PasswordGenerationService;
2018-01-09 23:45:17 +01:00
totpService: TotpServiceAbstraction;
2017-12-05 23:04:30 +01:00
autofillService: AutofillService;
containerService: ContainerService;
2017-12-05 23:04:30 +01:00
onUpdatedRan: boolean;
onReplacedRan: boolean;
2017-12-07 21:36:24 +01:00
loginToAutoFill: any = null;
loginsToAdd: any[] = [];
private commandsBackground: CommandsBackground;
2017-12-07 22:02:15 +01:00
private contextMenusBackground: ContextMenusBackground;
private idleBackground: IdleBackground;
private runtimeBackground: RuntimeBackground;
private tabsBackground: TabsBackground;
private webRequestBackground: WebRequestBackground;
private windowsBackground: WindowsBackground;
2017-12-05 23:04:30 +01:00
private sidebarAction: any;
private buildingContextMenu: boolean;
private menuOptionsLoaded: any[] = [];
private syncTimeout: number;
2017-12-05 23:04:30 +01:00
constructor() {
2017-12-05 23:04:30 +01:00
// Services
2018-01-09 20:26:20 +01:00
this.utilsService = new UtilsService();
2018-01-05 22:38:50 +01:00
this.platformUtilsService = new BrowserPlatformUtilsService();
2018-01-05 22:30:15 +01:00
this.messagingService = new BrowserMessagingService(this.platformUtilsService);
this.storageService = new BrowserStorageService(this.platformUtilsService);
this.i18nService = i18nService(this.platformUtilsService);
this.constantsService = new ConstantsService(this.i18nService, this.platformUtilsService);
2018-01-09 20:26:20 +01:00
this.cryptoService = new CryptoService(this.storageService,
this.storageService);
2018-01-04 16:51:08 +01:00
this.tokenService = new TokenService(this.storageService);
this.appIdService = new AppIdService(this.storageService);
2018-01-05 22:30:15 +01:00
this.apiService = new ApiService(this.tokenService, this.platformUtilsService,
2017-12-15 15:58:26 +01:00
(expired: boolean) => this.logout(expired));
2018-01-04 16:51:08 +01:00
this.environmentService = new EnvironmentService(this.apiService, this.storageService);
this.userService = new UserService(this.tokenService, this.storageService);
this.settingsService = new SettingsService(this.userService, this.storageService);
this.cipherService = new CipherService(this.cryptoService, this.userService, this.settingsService,
2018-01-04 16:51:08 +01:00
this.apiService, this.storageService);
2017-12-06 20:32:17 +01:00
this.folderService = new FolderService(this.cryptoService, this.userService, this.i18nService,
2018-01-04 16:51:08 +01:00
this.apiService, this.storageService);
this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService);
2017-12-05 23:04:30 +01:00
this.lockService = new LockService(this.cipherService, this.folderService, this.collectionService,
2018-01-05 22:30:15 +01:00
this.cryptoService, this.platformUtilsService, this.storageService,
2018-01-04 16:51:08 +01:00
() => this.setIcon(), () => this.refreshBadgeAndMenu());
2017-12-05 23:04:30 +01:00
this.syncService = new SyncService(this.userService, this.apiService, this.settingsService,
2017-12-07 04:28:33 +01:00
this.folderService, this.cipherService, this.cryptoService, this.collectionService,
2018-01-04 22:06:00 +01:00
this.storageService, this.messagingService, (expired: boolean) => this.logout(expired));
2018-01-04 16:51:08 +01:00
this.passwordGenerationService = new PasswordGenerationService(this.cryptoService, this.storageService);
this.totpService = new TotpService(this.storageService);
2017-12-05 23:04:30 +01:00
this.autofillService = new AutofillService(this.cipherService, this.tokenService,
2018-01-05 22:30:15 +01:00
this.totpService, this.utilsService, this.platformUtilsService);
this.containerService = new ContainerService(this.cryptoService, this.platformUtilsService);
2017-12-05 23:04:30 +01:00
// Other fields
this.sidebarAction = (typeof opr !== 'undefined') && opr.sidebarAction ?
2017-12-05 23:18:20 +01:00
opr.sidebarAction : (window as any).chrome.sidebarAction;
// Background
this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService);
2017-12-07 22:02:15 +01:00
this.contextMenusBackground = new ContextMenusBackground(this, this.cipherService,
this.passwordGenerationService);
this.idleBackground = new IdleBackground(this, this.lockService, this.storageService);
2018-01-05 22:30:15 +01:00
this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService,
this.platformUtilsService);
this.tabsBackground = new TabsBackground(this);
2018-01-05 22:30:15 +01:00
this.webRequestBackground = new WebRequestBackground(this.platformUtilsService, this.cipherService);
this.windowsBackground = new WindowsBackground(this);
2017-12-05 23:04:30 +01:00
}
2017-12-06 05:37:32 +01:00
async bootstrap() {
this.containerService.attachToWindow(window);
await this.commandsBackground.init();
2017-12-07 22:02:15 +01:00
await this.contextMenusBackground.init();
await this.idleBackground.init();
await this.runtimeBackground.init();
await this.tabsBackground.init();
await this.webRequestBackground.init();
await this.windowsBackground.init();
2017-12-06 05:37:32 +01:00
await this.environmentService.setUrlsFromStorage();
2017-12-07 04:28:33 +01:00
await this.setIcon();
2017-12-06 05:37:32 +01:00
this.cleanupLoginsToAdd();
2017-12-07 04:28:33 +01:00
await this.fullSync(true);
}
2017-12-07 22:02:15 +01:00
async setIcon() {
if (!chrome.browserAction && !this.sidebarAction) {
return;
}
const isAuthenticated = await this.userService.isAuthenticated();
const key = await this.cryptoService.getKey();
let suffix = '';
if (!isAuthenticated) {
suffix = '_gray';
} else if (!key) {
suffix = '_locked';
}
await this.actionSetIcon(chrome.browserAction, suffix);
await this.actionSetIcon(this.sidebarAction, suffix);
}
async refreshBadgeAndMenu() {
if (!chrome.windows || !chrome.contextMenus) {
return;
}
const tab = await BrowserApi.getTabFromCurrentWindowId();
if (!tab) {
return;
}
2018-01-04 16:51:08 +01:00
const disabled = await this.storageService.get<boolean>(ConstantsService.disableContextMenuItemKey);
2017-12-07 22:02:15 +01:00
if (!disabled) {
await this.buildContextMenu();
await this.contextMenuReady(tab, true);
} else {
await this.contextMenusRemoveAll();
await this.contextMenuReady(tab, false);
}
}
async logout(expired: boolean) {
const userId = await this.userService.getUserId();
await Promise.all([
this.syncService.setLastSync(new Date(0)),
this.tokenService.clearToken(),
this.cryptoService.clearKeys(),
this.userService.clear(),
this.settingsService.clear(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.passwordGenerationService.clear(),
]);
2018-01-04 22:06:00 +01:00
this.messagingService.send('doneLoggingOut', { expired: expired });
2017-12-07 22:02:15 +01:00
await this.setIcon();
await this.refreshBadgeAndMenu();
}
collectPageDetailsForContentScript(tab: any, sender: string, frameId: number = null) {
if (tab == null || !tab.id) {
return;
}
const options: any = {};
if (frameId != null) {
options.frameId = frameId;
}
chrome.tabs.sendMessage(tab.id, {
command: 'collectPageDetails',
tab: tab,
sender: sender,
}, options, () => {
if (chrome.runtime.lastError) {
return;
}
});
}
async checkLoginsToAdd(tab: any = null): Promise<any> {
if (!this.loginsToAdd.length) {
return;
}
if (tab != null) {
this.doCheck(tab);
return;
}
const currentTab = await BrowserApi.getTabFromCurrentWindow();
if (currentTab != null) {
this.doCheck(currentTab);
}
}
2017-12-05 23:04:30 +01:00
private async buildContextMenu() {
if (!chrome.contextMenus || this.buildingContextMenu) {
return;
}
this.buildingContextMenu = true;
await this.contextMenusRemoveAll();
await this.contextMenusCreate({
type: 'normal',
id: 'root',
contexts: ['all'],
title: 'bitwarden',
2017-12-05 23:04:30 +01:00
});
await this.contextMenusCreate({
type: 'normal',
id: 'autofill',
parentId: 'root',
contexts: ['all'],
title: this.i18nService.autoFill,
2017-12-05 23:04:30 +01:00
});
// Firefox & Edge do not support writing to the clipboard from background
2018-01-05 22:30:15 +01:00
if (!this.platformUtilsService.isFirefox() && !this.platformUtilsService.isEdge()) {
2017-12-05 23:04:30 +01:00
await this.contextMenusCreate({
type: 'normal',
id: 'copy-username',
parentId: 'root',
contexts: ['all'],
title: this.i18nService.copyUsername,
2017-12-05 23:04:30 +01:00
});
await this.contextMenusCreate({
type: 'normal',
id: 'copy-password',
parentId: 'root',
contexts: ['all'],
title: this.i18nService.copyPassword,
2017-12-05 23:04:30 +01:00
});
await this.contextMenusCreate({
type: 'separator',
parentId: 'root',
2017-12-05 23:04:30 +01:00
});
await this.contextMenusCreate({
type: 'normal',
id: 'generate-password',
parentId: 'root',
contexts: ['all'],
title: this.i18nService.generatePasswordCopied,
2017-12-05 23:04:30 +01:00
});
}
this.buildingContextMenu = false;
}
private async contextMenuReady(tab: any, contextMenuEnabled: boolean) {
await this.loadMenuAndUpdateBadge(tab.url, tab.id, contextMenuEnabled);
this.onUpdatedRan = this.onReplacedRan = false;
}
private async loadMenuAndUpdateBadge(url: string, tabId: number, contextMenuEnabled: boolean) {
if (!url || (!chrome.browserAction && !this.sidebarAction)) {
return;
}
2018-01-05 22:30:15 +01:00
const tabDomain = this.platformUtilsService.getDomain(url);
2017-12-05 23:04:30 +01:00
if (tabDomain == null) {
return;
}
this.actionSetBadgeBackgroundColor(chrome.browserAction);
this.actionSetBadgeBackgroundColor(this.sidebarAction);
this.menuOptionsLoaded = [];
try {
const ciphers = await this.cipherService.getAllDecryptedForDomain(tabDomain);
ciphers.sort(this.cipherService.sortCiphersByLastUsedThenName);
if (contextMenuEnabled) {
ciphers.forEach((cipher) => {
this.loadLoginContextMenuOptions(cipher);
2017-12-05 23:04:30 +01:00
});
}
let theText = '';
2017-12-05 23:04:30 +01:00
if (ciphers.length > 0 && ciphers.length < 9) {
theText = ciphers.length.toString();
} else if (ciphers.length > 0) {
theText = '9+';
} else {
if (contextMenuEnabled) {
2017-12-07 04:10:02 +01:00
await this.loadNoLoginsContextMenuOptions(this.i18nService.noMatchingLogins);
2017-12-05 23:04:30 +01:00
}
}
this.browserActionSetBadgeText(theText, tabId);
this.sidebarActionSetBadgeText(theText, tabId);
} catch (e) {
if (contextMenuEnabled) {
2017-12-07 04:10:02 +01:00
await this.loadNoLoginsContextMenuOptions(this.i18nService.vaultLocked);
2017-12-05 23:04:30 +01:00
}
this.browserActionSetBadgeText('', tabId);
this.sidebarActionSetBadgeText('', tabId);
}
}
private async loadLoginContextMenuOptions(cipher: any) {
2018-01-09 20:26:20 +01:00
if (cipher == null || cipher.type !== CipherType.Login) {
2017-12-05 23:04:30 +01:00
return;
}
let title = cipher.name;
if (cipher.login.username && cipher.login.username !== '') {
title += (' (' + cipher.login.username + ')');
}
2017-12-05 23:04:30 +01:00
await this.loadContextMenuOptions(title, cipher.id, cipher);
}
private async loadNoLoginsContextMenuOptions(noLoginsMessage: string) {
await this.loadContextMenuOptions(noLoginsMessage, 'noop', null);
}
private async loadContextMenuOptions(title: string, idSuffix: string, cipher: any) {
if (!chrome.contextMenus || this.menuOptionsLoaded.indexOf(idSuffix) > -1 ||
2018-01-09 20:26:20 +01:00
(cipher != null && cipher.type !== CipherType.Login)) {
2017-12-05 23:04:30 +01:00
return;
}
this.menuOptionsLoaded.push(idSuffix);
2017-12-07 04:10:02 +01:00
if (cipher == null || (cipher.login.password && cipher.login.password !== '')) {
2017-12-05 23:04:30 +01:00
await this.contextMenusCreate({
type: 'normal',
id: 'autofill_' + idSuffix,
parentId: 'autofill',
contexts: ['all'],
title: title,
2017-12-05 23:04:30 +01:00
});
}
2018-01-05 22:30:15 +01:00
if (this.platformUtilsService.isFirefox()) {
2017-12-05 23:04:30 +01:00
// Firefox does not support writing to the clipboard from background
return;
}
2017-12-07 04:10:02 +01:00
if (cipher == null || (cipher.login.username && cipher.login.username !== '')) {
2017-12-05 23:04:30 +01:00
await this.contextMenusCreate({
type: 'normal',
id: 'copy-username_' + idSuffix,
parentId: 'copy-username',
contexts: ['all'],
title: title,
2017-12-05 23:04:30 +01:00
});
}
2017-12-07 04:10:02 +01:00
if (cipher == null || (cipher.login.password && cipher.login.password !== '')) {
2017-12-05 23:04:30 +01:00
await this.contextMenusCreate({
type: 'normal',
id: 'copy-password_' + idSuffix,
parentId: 'copy-password',
contexts: ['all'],
title: title,
2017-12-05 23:04:30 +01:00
});
}
}
private cleanupLoginsToAdd() {
for (let i = this.loginsToAdd.length - 1; i >= 0; i--) {
if (this.loginsToAdd[i].expires < new Date()) {
this.loginsToAdd.splice(i, 1);
}
}
setTimeout(() => this.cleanupLoginsToAdd(), 2 * 60 * 1000); // check every 2 minutes
}
private doCheck(tab: any) {
if (tab == null) {
return;
}
2018-01-05 22:30:15 +01:00
const tabDomain = this.platformUtilsService.getDomain(tab.url);
if (tabDomain == null) {
return;
}
for (let i = 0; i < this.loginsToAdd.length; i++) {
if (this.loginsToAdd[i].tabId !== tab.id || this.loginsToAdd[i].domain !== tabDomain) {
continue;
}
2017-12-07 21:36:24 +01:00
BrowserApi.tabSendMessage(tab, 'openNotificationBar', {
type: 'add',
});
break;
}
}
private async fullSync(override: boolean = false) {
const syncInternal = 6 * 60 * 60 * 1000; // 6 hours
const lastSync = await this.syncService.getLastSync();
let lastSyncAgo = syncInternal + 1;
if (lastSync != null) {
lastSyncAgo = new Date().getTime() - lastSync.getTime();
}
if (override || lastSyncAgo >= syncInternal) {
await this.syncService.fullSync(override);
this.scheduleNextSync();
} else {
this.scheduleNextSync();
}
}
private scheduleNextSync() {
if (this.syncTimeout) {
clearTimeout(this.syncTimeout);
}
this.syncTimeout = setTimeout(async () => await this.fullSync(), 5 * 60 * 1000); // check every 5 minutes
}
// Browser API Helpers
2017-12-05 23:04:30 +01:00
private contextMenusRemoveAll() {
return new Promise((resolve) => {
chrome.contextMenus.removeAll(() => {
2017-12-05 23:04:30 +01:00
resolve();
if (chrome.runtime.lastError) {
return;
}
});
});
}
private contextMenusCreate(options: any) {
return new Promise((resolve) => {
chrome.contextMenus.create(options, () => {
2017-12-05 23:04:30 +01:00
resolve();
if (chrome.runtime.lastError) {
return;
}
});
});
}
private async actionSetIcon(theAction: any, suffix: string): Promise<any> {
2017-12-05 23:04:30 +01:00
if (!theAction || !theAction.setIcon) {
return;
2017-12-05 23:04:30 +01:00
}
const options = {
path: {
19: 'images/icon19' + suffix + '.png',
38: 'images/icon38' + suffix + '.png',
},
};
2018-01-05 22:30:15 +01:00
if (this.platformUtilsService.isFirefox()) {
await theAction.setIcon(options);
} else {
return new Promise((resolve) => {
theAction.setIcon(options, () => resolve());
2017-12-05 23:04:30 +01:00
});
}
2017-12-05 23:04:30 +01:00
}
private actionSetBadgeBackgroundColor(action: any) {
if (action && action.setBadgeBackgroundColor) {
action.setBadgeBackgroundColor({ color: '#294e5f' });
}
}
private browserActionSetBadgeText(text: string, tabId: number) {
if (chrome.browserAction && chrome.browserAction.setBadgeText) {
chrome.browserAction.setBadgeText({
text: text,
tabId: tabId,
2017-12-05 23:04:30 +01:00
});
}
}
private sidebarActionSetBadgeText(text: string, tabId: number) {
if (!this.sidebarAction) {
return;
}
if (this.sidebarAction.setBadgeText) {
this.sidebarAction.setBadgeText({
text: text,
tabId: tabId,
2017-12-05 23:04:30 +01:00
});
} else if (this.sidebarAction.setTitle) {
let title = 'bitwarden';
if (text && text !== '') {
title += (' [' + text + ']');
}
this.sidebarAction.setTitle({
title: title,
tabId: tabId,
2017-12-05 23:04:30 +01:00
});
}
}
}