diff --git a/src/background.d.ts b/src/background.d.ts index fbf7b011a6..5e5e2f9e4e 100644 --- a/src/background.d.ts +++ b/src/background.d.ts @@ -1,3 +1,4 @@ declare function escape(s: string): string; declare function unescape(s: string): string; declare var forge: any; +declare var opr: any; diff --git a/src/background.js b/src/background.js index e132a62069..57c0c893cc 100644 --- a/src/background.js +++ b/src/background.js @@ -8,7 +8,7 @@ import CryptoService from './services/crypto.service'; import EnvironmentService from './services/environment.service'; import FolderService from './services/folder.service'; import i18nService from './services/i18nService.js'; -import LockService from './services/lockService.js'; +import LockService from './services/lock.service'; import PasswordGenerationService from './services/passwordGeneration.service'; import SettingsService from './services/settings.service'; import SyncService from './services/sync.service'; @@ -103,8 +103,7 @@ var bg_isBackground = true, window.bg_settingsService = bg_settingsService = new SettingsService(bg_userService); window.bg_cipherService = bg_cipherService = new CipherService(bg_cryptoService, bg_userService, bg_settingsService, bg_apiService); window.bg_folderService = bg_folderService = new FolderService(bg_cryptoService, bg_userService, bg_i18nService, bg_apiService); - window.bg_lockService = bg_lockService = new LockService(bg_constantsService, bg_cryptoService, bg_folderService, bg_cipherService, bg_utilsService, - setIcon, refreshBadgeAndMenu); + window.bg_lockService = bg_lockService = new LockService(bg_cipherService, bg_folderService, bg_cryptoService, bg_utilsService, setIcon, refreshBadgeAndMenu); window.bg_syncService = bg_syncService = new SyncService(bg_userService, bg_apiService, bg_settingsService, bg_folderService, bg_cipherService, bg_cryptoService, logout); window.bg_passwordGenerationService = bg_passwordGenerationService = new PasswordGenerationService(bg_cryptoService); window.bg_totpService = bg_totpService = new TotpService(); diff --git a/src/services/lock.service.ts b/src/services/lock.service.ts new file mode 100644 index 0000000000..40295dab57 --- /dev/null +++ b/src/services/lock.service.ts @@ -0,0 +1,87 @@ +import CipherService from './cipher.service'; +import ConstantsService from './constants.service'; +import CryptoService from './crypto.service'; +import FolderService from './folder.service'; +import UtilsService from './utils.service'; + +export default class LockService { + constructor(private cipherService: CipherService, private folderService: FolderService, + private cryptoService: CryptoService, private utilsService: UtilsService, + private setIcon: Function, private refreshBadgeAndMenu: Function) { + this.checkLock(); + setInterval(() => this.checkLock(), 10 * 1000); // check every 10 seconds + + const self = this; + if ((window as any).chrome.idle && (window as any).chrome.idle.onStateChanged) { + (window as any).chrome.idle.onStateChanged.addListener(async (newState: string) => { + if (newState === 'locked') { + const lockOption = await UtilsService.getObjFromStorage(ConstantsService.lockOptionKey); + if (lockOption === -2) { + self.lock(); + } + } + }); + } + } + + async checkLock(): Promise { + const popupOpen = chrome.extension.getViews({ type: 'popup' }).length > 0; + const tabOpen = chrome.extension.getViews({ type: 'tab' }).length > 0; + const sidebarView = this.sidebarViewName(); + const sidebarOpen = sidebarView != null && chrome.extension.getViews({ type: sidebarView }).length > 0; + + if (popupOpen || tabOpen || sidebarOpen) { + // Do not lock + return; + } + + const key = await this.cryptoService.getKey(); + if (key == null) { + // no key so no need to lock + return; + } + + const lockOption = await UtilsService.getObjFromStorage(ConstantsService.lockOptionKey); + if (lockOption == null || lockOption < 0) { + return; + } + + const lastActive = await UtilsService.getObjFromStorage(ConstantsService.lastActiveKey); + if (lastActive == null) { + return; + } + + const lockOptionSeconds = lockOption * 60; + const diffSeconds = ((new Date()).getTime() - lastActive) / 1000; + if (diffSeconds >= lockOptionSeconds) { + // need to lock now + await this.lock(); + } + } + + async lock(): Promise { + await Promise.all([ + this.cryptoService.clearKey(), + this.cryptoService.clearOrgKeys(true), + this.cryptoService.clearPrivateKey(true), + this.cryptoService.clearEncKey(true), + ]); + + this.setIcon(); + this.folderService.clearCache(); + this.cipherService.clearCache(); + this.refreshBadgeAndMenu(); + } + + // Helpers + + private sidebarViewName(): string { + if ((window as any).chrome.sidebarAction && this.utilsService.isFirefox()) { + return 'sidebar'; + } else if (this.utilsService.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) { + return 'sidebar_panel'; + } + + return null; + } +} diff --git a/src/services/lockService.js b/src/services/lockService.js deleted file mode 100644 index 3e24c1c4f2..0000000000 --- a/src/services/lockService.js +++ /dev/null @@ -1,148 +0,0 @@ -export default function LockService(constantsService, cryptoService, folderService, cipherService, utilsService, setIcon, refreshBadgeAndMenu) { - this.lastLockCheck = null; - this.constantsService = constantsService; - this.cryptoService = cryptoService; - this.folderService = folderService; - this.cipherService = cipherService; - this.utilsService = utilsService; - this.setIcon = setIcon; - this.refreshBadgeAndMenu = refreshBadgeAndMenu; - - initLockService(this); -} - -function initLockService(self) { - checkLock(); - setInterval(checkLock, 10 * 1000); // check every 10 seconds - - function checkLock() { - var now = new Date(); - if (self.lastLockCheck && (now - self.lastLockCheck) < 5000) { - // can only check lock every 5 seconds - return; - } - self.lastLockCheck = now; - - var popupOpen = chrome.extension.getViews({ type: 'popup' }).length > 0; - var tabOpen = chrome.extension.getViews({ type: 'tab' }).length > 0; - var sidebarView = sidebarViewName(self.utilsService); - var sidebarOpen = sidebarView && chrome.extension.getViews({ type: sidebarView }).length > 0; - - if (popupOpen || tabOpen || sidebarOpen) { - // Do not lock - return; - } - - var lockOptionSeconds = null; - self.cryptoService.getKey().then(function (key) { - if (!key) { - // no key so no need to lock - return false; - } - - return getLockOption(); - }).then(function (lockOption) { - if (lockOption === false || lockOption < 0) { - return; - } - - lockOptionSeconds = lockOption * 60; - return getLastActive(); - }).then(function (lastActive) { - if (lockOptionSeconds === null) { - return; - } - - var diffSeconds = ((new Date()).getTime() - lastActive) / 1000; - if (diffSeconds >= lockOptionSeconds) { - // need to lock now - return self.lock(); - } - }); - } - - if (chrome.idle && chrome.idle.onStateChanged) { - chrome.idle.onStateChanged.addListener(function (newState) { - if (newState === 'locked') { - getLockOption().then(function (lockOption) { - if (lockOption !== -2) { - return; - } - - return self.lock(); - }); - } - }); - } - - LockService.prototype.lock = function () { - return Q.all([ - self.cryptoService.clearKey(), - self.cryptoService.clearOrgKeys(true), - self.cryptoService.clearPrivateKey(true), - self.cryptoService.clearEncKey(true) - ]).then(function () { - self.setIcon(); - self.folderService.clearCache(); - self.cipherService.clearCache(); - self.refreshBadgeAndMenu(); - }); - }; - - function getLockOption() { - var deferred = Q.defer(); - - chrome.storage.local.get(self.constantsService.lockOptionKey, function (obj) { - if (obj && obj[self.constantsService.lockOptionKey] === 0 || obj[self.constantsService.lockOptionKey]) { - deferred.resolve(parseInt(obj[self.constantsService.lockOptionKey])); - } - else { - deferred.reject(); - } - }); - - return deferred.promise; - } - - function getLastActive() { - var deferred = Q.defer(); - - chrome.storage.local.get(self.constantsService.lastActiveKey, function (obj) { - if (obj && obj[self.constantsService.lastActiveKey]) { - deferred.resolve(obj[self.constantsService.lastActiveKey]); - } - else { - deferred.reject(); - } - }); - - return deferred.promise; - } - - function getIdleState(detectionInterval) { - detectionInterval = detectionInterval || (60 * 5); - var deferred = Q.defer(); - - if (chrome.idle && chrome.idle.queryState) { - chrome.idle.queryState(detectionInterval, function (state) { - deferred.resolve(state); - }); - } - else { - deferred.resolve('active'); - } - - return deferred.promise; - } - - function sidebarViewName(utilsService) { - if (chrome.sidebarAction && utilsService.isFirefox()) { - return 'sidebar'; - } - else if (utilsService.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) { - return 'sidebar_panel'; - } - - return null; - } -} diff --git a/src/services/utils.service.ts b/src/services/utils.service.ts index 20b7be18b0..24517fd12d 100644 --- a/src/services/utils.service.ts +++ b/src/services/utils.service.ts @@ -163,7 +163,7 @@ export default class UtilsService { static getObjFromStorage(key: string): Promise { return new Promise((resolve) => { chrome.storage.local.get(key, (obj: any) => { - if (obj && obj[key]) { + if (obj && (typeof obj[key] !== 'undefined') && obj[key] !== null) { resolve(obj[key] as T); } else { resolve(null); @@ -252,7 +252,7 @@ export default class UtilsService { if (navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1) { this.browserCache = BrowserType.Firefox; - } else if ((!!(window as any).opr && !!(window as any).opr.addons) || !!(window as any).opera || + } else if ((!!(window as any).opr && !!opr.addons) || !!(window as any).opera || navigator.userAgent.indexOf(' OPR/') >= 0) { this.browserCache = BrowserType.Opera; } else if (navigator.userAgent.indexOf(' Edge/') !== -1) {