diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 6fe551e5136..5a682eb6b1e 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -382,5 +382,61 @@ "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead.", "description": "Your web browser does not support easy clipboard copying. Copy it manually instead." + }, + "verifyMasterPassword": { + "message": "Verify Master Password", + "description": "Verify Master Password" + }, + "invalidMasterPassword": { + "message": "Invalid master password", + "description": "Invalid master password" + }, + "errorsHaveOccurred": { + "message": "Invalid master password", + "description": "Invalid master password" + }, + "lockOptions": { + "message": "Lock Options", + "description": "Lock Options" + }, + "immediately": { + "message": "Immediately", + "description": "Immediately" + }, + "oneMinute": { + "message": "1 minute", + "description": "1 minute" + }, + "fiveMinutes": { + "message": "5 minutes", + "description": "5 minutes" + }, + "fifteenMinutes": { + "message": "15 minutes", + "description": "15 minutes" + }, + "thirtyMinutes": { + "message": "30 minutes", + "description": "30 minutes" + }, + "oneHour": { + "message": "1 hour", + "description": "1 hour" + }, + "fourHours": { + "message": "4 hours", + "description": "4 hours" + }, + "onRestart": { + "message": "On Restart", + "description": "On Restart" + }, + "never": { + "message": "Never", + "description": "Never" + }, + "security": { + "message": "Security", + "description": "Security" } } diff --git a/src/background.js b/src/background.js index db22f4cbd9c..314a9e9b09b 100644 --- a/src/background.js +++ b/src/background.js @@ -1,7 +1,8 @@ var isBackground = true; var i18nService = new i18nService(); +var constantsService = new ConstantsService(); var utilsService = new UtilsService(); -var cryptoService = new CryptoService(); +var cryptoService = new CryptoService(constantsService); var tokenService = new TokenService(); var apiService = new ApiService(tokenService); var userService = new UserService(tokenService, apiService, cryptoService); diff --git a/src/manifest.json b/src/manifest.json index 0e671a58926..884ca57b3f8 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -33,6 +33,7 @@ "models/dataModels.js", "models/domainModels.js", "services/i18nService.js", + "services/constantsService.js", "services/utilsService.js", "services/cryptoService.js", "services/tokenService.js", diff --git a/src/popup/app/accounts/views/accountsLogin.html b/src/popup/app/accounts/views/accountsLogin.html index 964c0d2b42f..148b53d6bea 100644 --- a/src/popup/app/accounts/views/accountsLogin.html +++ b/src/popup/app/accounts/views/accountsLogin.html @@ -7,7 +7,7 @@ -
{{i18n.bitwarden}}
+
{{i18n.appName}}
diff --git a/src/popup/app/app.js b/src/popup/app/app.js index 6c203b27991..e6e3916fb89 100644 --- a/src/popup/app/app.js +++ b/src/popup/app/app.js @@ -14,5 +14,6 @@ 'bit.current', 'bit.vault', 'bit.settings', - 'bit.tools' + 'bit.tools', + 'bit.lock' ]); diff --git a/src/popup/app/config.js b/src/popup/app/config.js index 3c942f7fc77..9976ee9bb2d 100644 --- a/src/popup/app/config.js +++ b/src/popup/app/config.js @@ -163,27 +163,41 @@ controller: 'settingsEditFolderController', data: { authorize: true }, params: { animation: null } + }) + .state('lock', { + url: '/lock', + templateUrl: 'app/lock/views/lock.html', + controller: 'lockController', + data: { authorize: true }, + params: { animation: null } }); }) - .run(function ($rootScope, userService, loginService, tokenService, $state) { + .run(function ($rootScope, userService, loginService, cryptoService, tokenService, $state) { $rootScope.$on('$stateChangeStart', function (event, toState, toParams) { - tokenService.getToken(function (token) { - userService.isAuthenticated(function (isAuthenticated) { - if (!toState.data || !toState.data.authorize) { - if (isAuthenticated && !tokenService.isTokenExpired(token)) { - event.preventDefault(); - $state.go('tabs.current'); + cryptoService.getKey(false, function (key) { + tokenService.getToken(function (token) { + userService.isAuthenticated(function (isAuthenticated) { + if (!toState.data || !toState.data.authorize) { + if (isAuthenticated && !tokenService.isTokenExpired(token)) { + event.preventDefault(); + if (!key) { + $state.go('lock'); + } + else { + $state.go('tabs.current'); + } + } + + return; } - return; - } - - if (!isAuthenticated || tokenService.isTokenExpired(token)) { - event.preventDefault(); - loginService.logOut(function () { - $state.go('home'); - }); - } + if (!isAuthenticated || tokenService.isTokenExpired(token)) { + event.preventDefault(); + loginService.logOut(function () { + $state.go('home'); + }); + } + }); }); }); }); diff --git a/src/popup/app/lock/lockController.js b/src/popup/app/lock/lockController.js new file mode 100644 index 00000000000..b321f52526f --- /dev/null +++ b/src/popup/app/lock/lockController.js @@ -0,0 +1,45 @@ +angular + .module('bit.lock') + + .controller('lockController', function ($scope, $state, $analytics, i18nService, loginService, cryptoService, toastr, + userService, SweetAlert) { + $scope.i18n = i18nService; + $('#master-password').focus(); + + $scope.logOut = function () { + loginService.logOut(function () { + SweetAlert.swal({ + title: 'Log Out', + text: 'Are you sure you want to log out?', + showCancelButton: true, + confirmButtonText: 'Yes', + cancelButtonText: 'Cancel' + }, function (confirmed) { + if (confirmed) { + loginService.logOut(function () { + $analytics.eventTrack('Logged Out'); + $state.go('home'); + }); + } + }); + }); + }; + + $scope.submit = function () { + userService.getEmail(function (email) { + var key = cryptoService.makeKey($scope.masterPassword, email); + cryptoService.hashPassword($scope.masterPassword, key, function (keyHash) { + cryptoService.getKeyHash(true, function (storedKeyHash) { + if (storedKeyHash && keyHash && storedKeyHash === keyHash) { + cryptoService.setKey(key, function () { + $state.go('tabs.current'); + }); + } + else { + toastr.error(i18nService.invalidMasterPassword, i18nService.errorsHaveOccurred); + } + }); + }); + }); + }; + }); diff --git a/src/popup/app/lock/lockModule.js b/src/popup/app/lock/lockModule.js new file mode 100644 index 00000000000..c4e0b817ba6 --- /dev/null +++ b/src/popup/app/lock/lockModule.js @@ -0,0 +1,2 @@ +angular + .module('bit.lock', ['ngAnimate', 'toastr']); diff --git a/src/popup/app/lock/views/lock.html b/src/popup/app/lock/views/lock.html new file mode 100644 index 00000000000..4a654cf5641 --- /dev/null +++ b/src/popup/app/lock/views/lock.html @@ -0,0 +1,25 @@ +
+
+
+ +
+
{{i18n.verifyMasterPassword}}
+
+
+
+
+
+
+ + + +
+
+
+
+

+ {{i18n.logOut}} +

+
+
diff --git a/src/popup/app/services/backgroundService.js b/src/popup/app/services/backgroundService.js index 38cde8bb859..e05e8da69f0 100644 --- a/src/popup/app/services/backgroundService.js +++ b/src/popup/app/services/backgroundService.js @@ -39,4 +39,7 @@ }) .factory('i18nService', function () { return chrome.extension.getBackgroundPage().i18nService; + }) + .factory('constantsService', function () { + return chrome.extension.getBackgroundPage().constantsService; }); diff --git a/src/popup/app/services/loginService.js b/src/popup/app/services/loginService.js index e9eab377d43..537f24fc127 100644 --- a/src/popup/app/services/loginService.js +++ b/src/popup/app/services/loginService.js @@ -19,17 +19,19 @@ tokenService.setToken(response.token, function () { cryptoService.setKey(key, function () { - if (response.profile) { - userService.setUserId(response.profile.id, function () { - userService.setEmail(response.profile.email, function () { - chrome.runtime.sendMessage({ command: 'loggedIn' }); - deferred.resolve(response); + cryptoService.setKeyHash(hashedPassword, function () { + if (response.profile) { + userService.setUserId(response.profile.id, function () { + userService.setEmail(response.profile.email, function () { + chrome.runtime.sendMessage({ command: 'loggedIn' }); + deferred.resolve(response); + }); }); - }); - } - else { - deferred.resolve(response); - } + } + else { + deferred.resolve(response); + } + }); }); }); }, function (error) { @@ -68,14 +70,16 @@ userService.getUserId(function (userId) { tokenService.clearToken(function () { cryptoService.clearKey(function () { - userService.clearUserId(function () { - userService.clearEmail(function () { - siteService.clear(userId, function () { - folderService.clear(userId, function () { - $rootScope.vaultSites = null; - $rootScope.vaultFolders = null; - chrome.runtime.sendMessage({ command: 'loggedOut' }); - callback(); + cryptoService.clearKeyHash(function () { + userService.clearUserId(function () { + userService.clearEmail(function () { + siteService.clear(userId, function () { + folderService.clear(userId, function () { + $rootScope.vaultSites = null; + $rootScope.vaultFolders = null; + chrome.runtime.sendMessage({ command: 'loggedOut' }); + callback(); + }); }); }); }); diff --git a/src/popup/app/settings/settingsController.js b/src/popup/app/settings/settingsController.js index bde39604e4d..6f5108bc2df 100644 --- a/src/popup/app/settings/settingsController.js +++ b/src/popup/app/settings/settingsController.js @@ -2,19 +2,42 @@ .module('bit.settings') .controller('settingsController', function ($scope, loginService, $state, SweetAlert, utilsService, $analytics, - i18nService) { - var gaKey = 'disableGa'; - + i18nService, constantsService, cryptoService) { utilsService.initListSectionItemListeners($(document), angular); $scope.disableGa = false; + $scope.lockOption = ''; $scope.i18n = i18nService; - chrome.storage.local.get(gaKey, function (obj) { - if (obj && obj[gaKey]) { + chrome.storage.local.get(constantsService.disableGaKey, function (obj) { + if (obj && obj[constantsService.disableGaKey]) { $scope.disableGa = true; } + else { + $scope.disableGa = false; + } }); + chrome.storage.local.get(constantsService.lockOptionKey, function (obj) { + if (obj && (obj[constantsService.lockOptionKey] || obj[constantsService.lockOptionKey] === 0)) { + $scope.lockOption = obj[constantsService.lockOptionKey].toString(); + } + else { + $scope.lockOption = ''; + } + }); + + $scope.changeLockOption = function () { + var obj = {}; + obj[constantsService.lockOptionKey] = null; + if ($scope.lockOption && $scope.lockOption !== '') { + obj[constantsService.lockOptionKey] = parseInt($scope.lockOption); + } + + chrome.storage.local.set(obj, function () { + cryptoService.toggleKey(function () { }); + }); + }; + $scope.logOut = function () { SweetAlert.swal({ title: 'Log Out', @@ -82,20 +105,20 @@ } $scope.updateGa = function () { - chrome.storage.local.get(gaKey, function (obj) { - if (obj[gaKey]) { + chrome.storage.local.get(constantsService.disableGaKey, function (obj) { + if (obj[constantsService.disableGaKey]) { // enable - obj[gaKey] = false; + obj[constantsService.disableGaKey] = false; } else { // disable $analytics.eventTrack('Disabled Google Analytics'); - obj[gaKey] = true; + obj[constantsService.disableGaKey] = true; } chrome.storage.local.set(obj, function () { - $scope.disableGa = obj[gaKey]; - if (!obj[gaKey]) { + $scope.disableGa = obj[constantsService.disableGaKey]; + if (!obj[constantsService.disableGaKey]) { $analytics.eventTrack('Enabled Google Analytics'); } }); diff --git a/src/popup/app/settings/views/settings.html b/src/popup/app/settings/views/settings.html index 948cb0e372e..1471c5694c9 100644 --- a/src/popup/app/settings/views/settings.html +++ b/src/popup/app/settings/views/settings.html @@ -3,6 +3,31 @@
+
+
+ {{i18n.security}} +
+
+
+ + +
+ + {{i18n.twoStepLogin}} + + +
+
{{i18n.account}} @@ -16,10 +41,6 @@ {{i18n.changeEmail}} - - {{i18n.twoStepLogin}} - - {{i18n.logOut}} diff --git a/src/popup/app/vault/views/vaultAddSite.html b/src/popup/app/vault/views/vaultAddSite.html index 5029b86b350..b22da4f2554 100644 --- a/src/popup/app/vault/views/vaultAddSite.html +++ b/src/popup/app/vault/views/vaultAddSite.html @@ -41,7 +41,7 @@
- +