1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 07:13:32 +00:00

convert to promises. loginService cipher refactor

This commit is contained in:
Kyle Spearrin
2017-10-13 17:07:20 -04:00
parent 294817d17b
commit 2b5915b257
15 changed files with 526 additions and 527 deletions

View File

@@ -32,14 +32,14 @@ var bg_isBackground = true,
bg_i18nService = new i18nService(bg_utilsService); bg_i18nService = new i18nService(bg_utilsService);
bg_constantsService = new ConstantsService(bg_i18nService); bg_constantsService = new ConstantsService(bg_i18nService);
bg_cryptoService = new CryptoService(bg_constantsService); bg_cryptoService = new CryptoService(bg_constantsService);
bg_tokenService = new TokenService(); bg_tokenService = new TokenService(bg_utilsService);
bg_appIdService = new AppIdService(); bg_appIdService = new AppIdService();
bg_apiService = new ApiService(bg_tokenService, bg_appIdService, bg_utilsService, bg_constantsService, logout); bg_apiService = new ApiService(bg_tokenService, bg_appIdService, bg_utilsService, bg_constantsService, logout);
bg_environmentService = new EnvironmentService(bg_constantsService, bg_apiService); bg_environmentService = new EnvironmentService(bg_constantsService, bg_apiService);
bg_userService = new UserService(bg_tokenService, bg_apiService, bg_cryptoService); bg_userService = new UserService(bg_tokenService, bg_apiService, bg_cryptoService, bg_utilsService);
bg_settingsService = new SettingsService(bg_userService); bg_settingsService = new SettingsService(bg_userService, bg_utilsService);
bg_loginService = new LoginService(bg_cryptoService, bg_userService, bg_apiService, bg_settingsService); bg_loginService = new LoginService(bg_cryptoService, bg_userService, bg_apiService, bg_settingsService, bg_utilsService);
bg_folderService = new FolderService(bg_cryptoService, bg_userService, bg_apiService, bg_i18nService); bg_folderService = new FolderService(bg_cryptoService, bg_userService, bg_apiService, bg_i18nService, bg_utilsService);
bg_lockService = new LockService(bg_constantsService, bg_cryptoService, bg_folderService, bg_loginService, bg_utilsService, bg_lockService = new LockService(bg_constantsService, bg_cryptoService, bg_folderService, bg_loginService, bg_utilsService,
setIcon, refreshBadgeAndMenu); setIcon, refreshBadgeAndMenu);
bg_syncService = new SyncService(bg_loginService, bg_folderService, bg_userService, bg_apiService, bg_settingsService, bg_syncService = new SyncService(bg_loginService, bg_folderService, bg_userService, bg_apiService, bg_settingsService,
@@ -807,27 +807,23 @@ var bg_isBackground = true,
} }
} }
// TODO: Fix callback hell by moving to promises
function logout(expired, callback) { function logout(expired, callback) {
bg_userService.getUserId(function (userId) { bg_syncService.setLastSync(new Date(0), function () {
bg_syncService.setLastSync(new Date(0), function () { bg_userService.getUserIdPromise().then(function (userId) {
bg_settingsService.clear(function () { return Q.all([
bg_tokenService.clearToken(function () { bg_tokenService.clearToken(),
bg_cryptoService.clearKeys(function () { bg_cryptoService.clearKeys(),
bg_userService.clear(function () { bg_userService.clear(),
bg_loginService.clear(userId, function () { bg_settingsService.clear(userId),
bg_folderService.clear(userId, function () { bg_loginService.clear(userId),
chrome.runtime.sendMessage({ bg_folderService.clear(userId)
command: 'doneLoggingOut', expired: expired ]).then(function () {
}); chrome.runtime.sendMessage({
setIcon(); command: 'doneLoggingOut', expired: expired
refreshBadgeAndMenu();
callback();
});
});
});
});
}); });
setIcon();
refreshBadgeAndMenu();
callback();
}); });
}); });
}); });

View File

@@ -7,12 +7,49 @@
this.favorite = cipher.favorite; this.favorite = cipher.favorite;
switch (type) { switch (type) {
case 1: // login case 1: // cipherType.login
this.login = { this.login = {
uri: cipher.uri ? cipher.uri.encryptedString : null, uri: cipher.login.uri ? cipher.login.uri.encryptedString : null,
username: cipher.username ? cipher.username.encryptedString : null, username: cipher.login.username ? cipher.login.username.encryptedString : null,
password: cipher.password ? cipher.password.encryptedString : null, password: cipher.login.password ? cipher.login.password.encryptedString : null,
totp: cipher.totp ? cipher.totp.encryptedString : null totp: cipher.login.totp ? cipher.login.totp.encryptedString : null
};
break;
case 2: // cipherType.secureNote
this.secureNote = {
type: cipher.secureNote.type
};
break;
case 3: // cipherType.card
this.card = {
cardholderName: cipher.card.cardholderName ? cipher.card.cardholderName.encryptedString : null,
brand: cipher.card.brand ? cipher.card.brand.encryptedString : null,
number: cipher.card.number ? cipher.card.number.encryptedString : null,
expMonth: cipher.card.expMonth ? cipher.card.expMonth.encryptedString : null,
expYear: cipher.card.expYear ? cipher.card.expYear.encryptedString : null,
code: cipher.card.code ? cipher.card.code.encryptedString : null
};
break;
case 4: // cipherType.identity
this.identity = {
title: cipher.identity.title ? cipher.identity.title.encryptedString : null,
firstName: cipher.identity.firstName ? cipher.identity.firstName.encryptedString : null,
middleName: cipher.identity.middleName ? cipher.identity.middleName.encryptedString : null,
lastName: cipher.identity.lastName ? cipher.identity.lastName.encryptedString : null,
address1: cipher.identity.address1 ? cipher.identity.address1.encryptedString : null,
address2: cipher.identity.address2 ? cipher.identity.address2.encryptedString : null,
address3: cipher.identity.address3 ? cipher.identity.address3.encryptedString : null,
city: cipher.identity.city ? cipher.identity.city.encryptedString : null,
state: cipher.identity.state ? cipher.identity.state.encryptedString : null,
postalCode: cipher.identity.postalCode ? cipher.identity.postalCode.encryptedString : null,
country: cipher.identity.country ? cipher.identity.country.encryptedString : null,
company: cipher.identity.company ? cipher.identity.company.encryptedString : null,
email: cipher.identity.email ? cipher.identity.email.encryptedString : null,
phone: cipher.identity.phone ? cipher.identity.phone.encryptedString : null,
ssn: cipher.identity.ssn ? cipher.identity.ssn.encryptedString : null,
username: cipher.identity.username ? cipher.identity.username.encryptedString : null,
passportNumber: cipher.identity.passportNumber ? cipher.identity.passportNumber.encryptedString : null,
licenseNumber: cipher.identity.licenseNumber ? cipher.identity.licenseNumber.encryptedString : null
}; };
break; break;
default: default:

View File

@@ -1,7 +1,7 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, $q, toastr, .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, toastr,
SweetAlert, utilsService, $analytics, i18nService, cryptoService, tokenService) { SweetAlert, utilsService, $analytics, i18nService, cryptoService, tokenService) {
$scope.i18n = i18nService; $scope.i18n = i18nService;
utilsService.initListSectionItemListeners($(document), angular); utilsService.initListSectionItemListeners($(document), angular);
@@ -10,44 +10,44 @@ angular
$scope.canAccessAttachments = $scope.isPremium; $scope.canAccessAttachments = $scope.isPremium;
$scope.hasUpdatedKey = false; $scope.hasUpdatedKey = false;
loginService.get($stateParams.id, function (login) { loginService.get($stateParams.id).then(function (login) {
$q.when(login.decrypt()).then(function (model) { return login.decrypt();
$scope.login = model; }).then(function (model) {
$scope.canAccessAttachments = $scope.isPremium || !!$scope.login.organizationId; $scope.login = model;
$scope.canAccessAttachments = $scope.isPremium || !!$scope.login.organizationId;
if (!$scope.canAccessAttachments) { if (!$scope.canAccessAttachments) {
SweetAlert.swal({ SweetAlert.swal({
title: i18nService.premiumRequired, title: i18nService.premiumRequired,
text: i18nService.premiumRequiredDesc, text: i18nService.premiumRequiredDesc,
showCancelButton: true, showCancelButton: true,
confirmButtonText: i18nService.learnMore, confirmButtonText: i18nService.learnMore,
cancelButtonText: i18nService.cancel cancelButtonText: i18nService.cancel
}, function (confirmed) { }, function (confirmed) {
if (confirmed) { if (confirmed) {
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' }); chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' });
} }
}); });
return; return;
} }
else { else {
cryptoService.getEncKey().then(function (key) { cryptoService.getEncKey().then(function (key) {
$scope.hasUpdatedKey = !!key; $scope.hasUpdatedKey = !!key;
if (!$scope.hasUpdatedKey) { if (!$scope.hasUpdatedKey) {
SweetAlert.swal({ SweetAlert.swal({
title: i18nService.featureUnavailable, title: i18nService.featureUnavailable,
text: i18nService.updateKey, text: i18nService.updateKey,
showCancelButton: true, showCancelButton: true,
confirmButtonText: i18nService.learnMore, confirmButtonText: i18nService.learnMore,
cancelButtonText: i18nService.cancel cancelButtonText: i18nService.cancel
}, function (confirmed) { }, function (confirmed) {
if (confirmed) { if (confirmed) {
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/update-encryption-key/' }); chrome.tabs.create({ url: 'https://help.bitwarden.com/article/update-encryption-key/' });
} }
}); });
} }
}); });
} }
});
}); });
$scope.submitPromise = null; $scope.submitPromise = null;
@@ -69,8 +69,8 @@ angular
return deferred.promise; return deferred.promise;
} }
$scope.submitPromise = $q.when(loginService.saveAttachmentWithServer($scope.login, files[0])).then(function (login) { $scope.submitPromise = loginService.saveAttachmentWithServer($scope.login, files[0]).then(function (login) {
$q.when(login.decrypt()).then(function (model) { login.decrypt().then(function (model) {
$scope.login = model; $scope.login = model;
}); });
$analytics.eventTrack('Added Attachment'); $analytics.eventTrack('Added Attachment');
@@ -100,7 +100,7 @@ angular
cancelButtonText: i18nService.no cancelButtonText: i18nService.no
}, function (confirmed) { }, function (confirmed) {
if (confirmed) { if (confirmed) {
$q.when(loginService.deleteAttachmentWithServer($stateParams.id, attachment.id)).then(function () { loginService.deleteAttachmentWithServer($stateParams.id, attachment.id).then(function () {
var index = $scope.login.attachments.indexOf(attachment); var index = $scope.login.attachments.indexOf(attachment);
if (index > -1) { if (index > -1) {
$scope.login.attachments.splice(index, 1); $scope.login.attachments.splice(index, 1);

View File

@@ -2,7 +2,7 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultEditLoginController', function ($scope, $state, $stateParams, loginService, folderService, .controller('vaultEditLoginController', function ($scope, $state, $stateParams, loginService, folderService,
cryptoService, $q, toastr, SweetAlert, utilsService, $analytics, i18nService, constantsService) { cryptoService, toastr, SweetAlert, utilsService, $analytics, i18nService, constantsService) {
$scope.i18n = i18nService; $scope.i18n = i18nService;
$scope.constants = constantsService; $scope.constants = constantsService;
$scope.showAttachments = !utilsService.isEdge(); $scope.showAttachments = !utilsService.isEdge();
@@ -21,14 +21,14 @@ angular
angular.extend($scope.login, $stateParams.login); angular.extend($scope.login, $stateParams.login);
} }
else { else {
loginService.get(loginId, function (login) { loginService.get(loginId).then(function (login) {
$q.when(login.decrypt()).then(function (model) { return login.decrypt();
$scope.login = model; }).then(function (model) {
}); $scope.login = model;
}); });
} }
$q.when(folderService.getAllDecrypted()).then(function (folders) { folderService.getAllDecrypted().then(function (folders) {
$scope.folders = folders; $scope.folders = folders;
}); });
@@ -41,9 +41,9 @@ angular
return; return;
} }
$scope.savePromise = $q.when(loginService.encrypt(model)).then(function (loginModel) { $scope.savePromise = loginService.encrypt(model).then(function (loginModel) {
var login = new Login(loginModel, true); var login = new Login(loginModel, true);
return $q.when(loginService.saveWithServer(login)).then(function (login) { return loginService.saveWithServer(login).then(function (login) {
$analytics.eventTrack('Edited Login'); $analytics.eventTrack('Edited Login');
toastr.success(i18nService.editedLogin); toastr.success(i18nService.editedLogin);
$scope.close(); $scope.close();
@@ -60,7 +60,7 @@ angular
cancelButtonText: i18nService.no cancelButtonText: i18nService.no
}, function (confirmed) { }, function (confirmed) {
if (confirmed) { if (confirmed) {
$q.when(loginService.deleteWithServer(loginId)).then(function () { loginService.deleteWithServer(loginId).then(function () {
$analytics.eventTrack('Deleted Login'); $analytics.eventTrack('Deleted Login');
toastr.success(i18nService.deletedLogin); toastr.success(i18nService.deletedLogin);
$state.go('tabs.vault', { $state.go('tabs.vault', {

View File

@@ -1,7 +1,7 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultViewLoginController', function ($scope, $state, $stateParams, loginService, toastr, $q, .controller('vaultViewLoginController', function ($scope, $state, $stateParams, loginService, toastr,
$analytics, i18nService, utilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert, $analytics, i18nService, utilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert,
constantsService) { constantsService) {
$scope.constants = constantsService; $scope.constants = constantsService;
@@ -12,45 +12,45 @@ angular
$scope.isPremium = tokenService.getPremium(); $scope.isPremium = tokenService.getPremium();
$scope.login = null; $scope.login = null;
loginService.get($stateParams.loginId, function (login) { loginService.get($stateParams.loginId).then(function (login) {
if (!login) { if (!login) {
return; return;
} }
$q.when(login.decrypt()).then(function (model) { return login.decrypt();
$scope.login = model; }).then(function (model) {
$scope.login = model;
if (model.password) { if (model.password) {
$scope.login.maskedPassword = $scope.maskValue(model.password); $scope.login.maskedPassword = $scope.maskValue(model.password);
} }
if (model.uri) { if (model.uri) {
$scope.login.showLaunch = model.uri.startsWith('http://') || model.uri.startsWith('https://'); $scope.login.showLaunch = model.uri.startsWith('http://') || model.uri.startsWith('https://');
var domain = utilsService.getDomain(model.uri); var domain = utilsService.getDomain(model.uri);
if (domain) { if (domain) {
$scope.login.website = domain; $scope.login.website = domain;
}
else {
$scope.login.website = model.uri;
}
} }
else { else {
$scope.login.showLaunch = false; $scope.login.website = model.uri;
}
}
else {
$scope.login.showLaunch = false;
}
if (model.totp && (login.organizationUseTotp || tokenService.getPremium())) {
totpUpdateCode();
totpTick();
if (totpInterval) {
clearInterval(totpInterval);
} }
if (model.totp && (login.organizationUseTotp || tokenService.getPremium())) { totpInterval = setInterval(function () {
totpUpdateCode();
totpTick(); totpTick();
}, 1000);
if (totpInterval) { }
clearInterval(totpInterval);
}
totpInterval = setInterval(function () {
totpTick();
}, 1000);
}
});
}); });
$scope.edit = function (login) { $scope.edit = function (login) {

View File

@@ -257,8 +257,10 @@ function initApiService() {
// Cipher APIs // Cipher APIs
ApiService.prototype.postCipher = function (cipherRequest, success, error) { ApiService.prototype.postCipher = function (cipherRequest) {
var self = this; var self = this,
deferred = Q.defer();
handleTokenState(self).then(function (tokenHeader) { handleTokenState(self).then(function (tokenHeader) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
@@ -268,19 +270,23 @@ function initApiService() {
dataType: 'json', dataType: 'json',
headers: tokenHeader, headers: tokenHeader,
success: function (response) { success: function (response) {
success(new CipherResponse(response)); deferred.resolve(new CipherResponse(response));
}, },
error: function (jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self); handleError(deferred.reject, jqXHR, false, self);
} }
}); });
}, function (jqXHR) { }, function (jqXHR) {
handleError(error, jqXHR, true, self); handleError(deferred.reject, jqXHR, true, self);
}); });
return deferred.promise;
}; };
ApiService.prototype.putCipher = function (id, cipherRequest, success, error) { ApiService.prototype.putCipher = function (id, cipherRequest) {
var self = this; var self = this,
deferred = Q.defer();
handleTokenState(self).then(function (tokenHeader) { handleTokenState(self).then(function (tokenHeader) {
$.ajax({ $.ajax({
type: 'PUT', type: 'PUT',
@@ -290,19 +296,23 @@ function initApiService() {
dataType: 'json', dataType: 'json',
headers: tokenHeader, headers: tokenHeader,
success: function (response) { success: function (response) {
success(new CipherResponse(response)); deferred.resolve(new CipherResponse(response));
}, },
error: function (jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self); handleError(deferred.reject, jqXHR, false, self);
} }
}); });
}, function (jqXHR) { }, function (jqXHR) {
handleError(error, jqXHR, true, self); handleError(deferred.reject, jqXHR, true, self);
}); });
return deferred.promise;
}; };
ApiService.prototype.deleteCipher = function (id, success, error) { ApiService.prototype.deleteCipher = function (id) {
var self = this; var self = this,
deferred = Q.defer();
handleTokenState(self).then(function (tokenHeader) { handleTokenState(self).then(function (tokenHeader) {
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
@@ -310,19 +320,23 @@ function initApiService() {
dataType: 'text', dataType: 'text',
headers: tokenHeader, headers: tokenHeader,
success: function (response) { success: function (response) {
success(); deferred.resolve(response);
}, },
error: function (jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self); handleError(deferred.reject, jqXHR, false, self);
} }
}); });
}, function (jqXHR) { }, function (jqXHR) {
handleError(error, jqXHR, true, self); handleError(deferred.reject, jqXHR, true, self);
}); });
return deferred.promise;
}; };
ApiService.prototype.postCipherAttachment = function (id, formData, success, error) { ApiService.prototype.postCipherAttachment = function (id, formData) {
var self = this; var self = this,
deferred = Q.defer();
handleTokenState(self).then(function (tokenHeader) { handleTokenState(self).then(function (tokenHeader) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
@@ -333,19 +347,23 @@ function initApiService() {
dataType: 'json', dataType: 'json',
headers: tokenHeader, headers: tokenHeader,
success: function (response) { success: function (response) {
success(new CipherResponse(response)); deferred.resolve(new CipherResponse(response));
}, },
error: function (jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self); handleError(deferred.reject, jqXHR, false, self);
} }
}); });
}, function (jqXHR) { }, function (jqXHR) {
handleError(error, jqXHR, true, self); handleError(deferred.reject, jqXHR, true, self);
}); });
return deferred.promise;
}; };
ApiService.prototype.deleteCipherAttachment = function (id, attachmentId, success, error) { ApiService.prototype.deleteCipherAttachment = function (id, attachmentId, success, error) {
var self = this; var self = this,
deferred = Q.defer();
handleTokenState(self).then(function (tokenHeader) { handleTokenState(self).then(function (tokenHeader) {
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
@@ -353,15 +371,17 @@ function initApiService() {
dataType: 'text', dataType: 'text',
headers: tokenHeader, headers: tokenHeader,
success: function (response) { success: function (response) {
success(); deferred.resolve(response);
}, },
error: function (jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self); handleError(deferred.reject, jqXHR, false, self);
} }
}); });
}, function (jqXHR) { }, function (jqXHR) {
handleError(error, jqXHR, true, self); handleError(deferred.reject, jqXHR, true, self);
}); });
return deferred.promise;
}; };
// Sync APIs // Sync APIs

View File

@@ -226,7 +226,7 @@ function initAutofill() {
didAutofill = true; didAutofill = true;
if (!options.skipLastUsed) { if (!options.skipLastUsed) {
self.loginService.updateLastUsedDate(options.login.id, function () { }); self.loginService.updateLastUsedDate(options.login.id);
} }
chrome.tabs.sendMessage(tab.id, { chrome.tabs.sendMessage(tab.id, {

View File

@@ -336,21 +336,15 @@ function initCryptoService(constantsService) {
return deferred.promise; return deferred.promise;
}; };
CryptoService.prototype.clearKeys = function (callback) { CryptoService.prototype.clearKeys = function () {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this; var self = this;
Q.all([ return Q.all([
self.clearKey(), self.clearKey(),
self.clearKeyHash(), self.clearKeyHash(),
self.clearOrgKeys(), self.clearOrgKeys(),
self.clearEncKey(), self.clearEncKey(),
self.clearPrivateKey() self.clearPrivateKey()
]).then(function () { ]);
callback();
});
}; };
CryptoService.prototype.toggleKey = function (callback) { CryptoService.prototype.toggleKey = function (callback) {

View File

@@ -1,8 +1,9 @@
function FolderService(cryptoService, userService, apiService, i18nService) { function FolderService(cryptoService, userService, apiService, i18nService, utilsService) {
this.cryptoService = cryptoService; this.cryptoService = cryptoService;
this.userService = userService; this.userService = userService;
this.apiService = apiService; this.apiService = apiService;
this.i18nService = i18nService; this.i18nService = i18nService;
this.utilsService = utilsService;
this.decryptedFolderCache = null; this.decryptedFolderCache = null;
initFolderService(); initFolderService();
@@ -171,33 +172,19 @@ function initFolderService() {
}); });
}; };
FolderService.prototype.replace = function (folders, callback) { FolderService.prototype.replace = function (folders) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this; var self = this;
self.userService.getUserIdPromise().then(function (userId) {
self.userService.getUserId(function (userId) { return self.utilsService.saveObjToStorage('folders_' + userId, folders);
var obj = {}; }).then(function () {
obj['folders_' + userId] = folders; self.decryptedFolderCache = null;
chrome.storage.local.set(obj, function () {
self.decryptedFolderCache = null;
callback();
});
}); });
}; };
FolderService.prototype.clear = function (userId, callback) { FolderService.prototype.clear = function (userId) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this; var self = this;
return self.utilsService.removeFromStorage('folders_' + userId).then(function () {
chrome.storage.local.remove('folders_' + userId, function () {
self.decryptedFolderCache = null; self.decryptedFolderCache = null;
callback();
}); });
}; };

View File

@@ -1,16 +1,18 @@
function LoginService(cryptoService, userService, apiService, settingsService) { function LoginService(cryptoService, userService, apiService, settingsService, utilsService) {
this.cryptoService = cryptoService; this.cryptoService = cryptoService;
this.userService = userService; this.userService = userService;
this.apiService = apiService; this.apiService = apiService;
this.settingsService = settingsService; this.settingsService = settingsService;
this.decryptedLoginCache = null; this.utilsService = utilsService;
this.decryptedCipherCache = null;
this.localDataKey = 'sitesLocalData';
initLoginService(); initLoginService();
} }
function initLoginService() { function initLoginService() {
LoginService.prototype.clearCache = function () { LoginService.prototype.clearCache = function () {
this.decryptedLoginCache = null; this.decryptedCipherCache = null;
}; };
LoginService.prototype.encrypt = function (login) { LoginService.prototype.encrypt = function (login) {
@@ -93,114 +95,98 @@ function initLoginService() {
}); });
}; };
LoginService.prototype.get = function (id, callback) { LoginService.prototype.get = function (id) {
if (!callback || typeof callback !== 'function') { var self = this,
throw 'callback function required'; key = null,
} localData;
this.userService.getUserId(function (userId) { return self.userService.getUserIdPromise().then(function (userId) {
var loginsKey = 'sites_' + userId; key = 'ciphers_' + userId;
var localDataKey = 'sitesLocalData'; return self.utilsService.getObjFromStorage(self.localDataKey);
}).then(function (data) {
localData = data;
if (!localData) {
localData = {};
}
return self.utilsService.getObjFromStorage(key);
}).then(function (ciphers) {
if (ciphers && id in ciphers) {
return new Login(ciphers[id], false, localData[id]);
}
chrome.storage.local.get(localDataKey, function (localDataObj) { return null;
var localData = localDataObj[localDataKey];
if (!localData) {
localData = {};
}
chrome.storage.local.get(loginsKey, function (obj) {
var logins = obj[loginsKey];
if (logins && id in logins) {
callback(new Login(logins[id], false, localData[id]));
return;
}
callback(null);
});
});
}); });
}; };
LoginService.prototype.getAll = function (callback) { LoginService.prototype.getAll = function () {
if (!callback || typeof callback !== 'function') { var self = this,
throw 'callback function required'; key = null,
} localData = null;
this.userService.getUserId(function (userId) { self.userService.getUserIdPromise().then(function (userId) {
var loginsKey = 'sites_' + userId; key = 'ciphers_' + userId;
var localDataKey = 'sitesLocalData'; return self.utilsService.getObjFromStorage(self.localDataKey);
}).then(function (data) {
chrome.storage.local.get(localDataKey, function (localDataObj) { localData = data;
var localData = localDataObj[localDataKey]; if (!localData) {
if (!localData) { localData = {};
localData = {}; }
return self.utilsService.getObjFromStorage(key);
}).then(function (logins) {
var response = [];
for (var id in logins) {
if (id) {
response.push(new Login(logins[id], false, localData[id]));
} }
}
chrome.storage.local.get(loginsKey, function (obj) { return response;
var logins = obj[loginsKey];
var response = [];
for (var id in logins) {
if (!id) {
continue;
}
response.push(new Login(logins[id], false, localData[id]));
}
callback(response);
});
});
}); });
}; };
LoginService.prototype.getAllDecrypted = function () { LoginService.prototype.getAllDecrypted = function () {
var deferred = Q.defer(); var self = this,
var self = this; decLogins = [];
self.cryptoService.getKey().then(function (key) { return self.cryptoService.getKey().then(function (key) {
if (!key) { if (!key) {
deferred.reject(); deferred.reject();
return; return;
} }
if (self.decryptedLoginCache) { if (self.decryptedCipherCache) {
deferred.resolve(self.decryptedLoginCache); deferred.resolve(self.decryptedCipherCache);
return; return;
} }
return self.getAll();
}).then(function (logins) {
var promises = []; var promises = [];
var decLogins = []; for (var i = 0; i < logins.length; i++) {
self.getAll(function (logins) { /* jshint ignore:start */
for (var i = 0; i < logins.length; i++) { promises.push(logins[i].decrypt().then(function (login) {
/* jshint ignore:start */ decLogins.push(login);
promises.push(logins[i].decrypt().then(function (login) { }));
decLogins.push(login); /* jshint ignore:end */
})); }
/* jshint ignore:end */
}
Q.all(promises).then(function () { return Q.all(promises);
self.decryptedLoginCache = decLogins; }).then(function () {
deferred.resolve(self.decryptedLoginCache); self.decryptedCipherCache = decLogins;
}); return self.decryptedCipherCache;
});
}); });
return deferred.promise;
}; };
LoginService.prototype.getAllDecryptedForFolder = function (folderId) { LoginService.prototype.getAllDecryptedForFolder = function (folderId) {
var self = this; return this.getAllDecrypted().then(function (ciphers) {
var ciphersToReturn = [];
return self.getAllDecrypted().then(function (logins) { for (var i = 0; i < ciphers.length; i++) {
var loginsToReturn = []; if (ciphers[i].folderId === folderId) {
for (var i = 0; i < logins.length; i++) { ciphersToReturn.push(ciphers[i]);
if (logins[i].folderId === folderId) {
loginsToReturn.push(logins[i]);
} }
} }
return loginsToReturn; return ciphersToReturn;
}); });
}; };
@@ -222,265 +208,222 @@ function initLoginService() {
return matchingDomains; return matchingDomains;
}); });
var loginsPromise = self.getAllDecrypted().then(function (logins) { return Q.all([eqDomainsPromise, self.getAllDecrypted()]).then(function (result) {
return logins; var matchingDomains = result[0],
}); ciphers = result[1],
ciphersToReturn = [];
return Q.all([eqDomainsPromise, loginsPromise]).then(function (result) { for (var i = 0; i < ciphers.length; i++) {
var matchingDomains = result[0]; if (ciphers[i].domain && matchingDomains.indexOf(ciphers[i].domain) > -1) {
var logins = result[1]; ciphersToReturn.push(ciphers[i]);
var loginsToReturn = [];
for (var i = 0; i < logins.length; i++) {
if (logins[i].domain && matchingDomains.indexOf(logins[i].domain) >= 0) {
loginsToReturn.push(logins[i]);
} }
} }
return loginsToReturn; return ciphersToReturn;
}); });
}; };
LoginService.prototype.getLastUsedForDomain = function (domain) { LoginService.prototype.getLastUsedForDomain = function (domain) {
var self = this; var self = this,
var deferred = Q.defer(); deferred = Q.defer();
self.getAllDecryptedForDomain(domain).then(function (logins) {
if (!logins.length) { self.getAllDecryptedForDomain(domain).then(function (ciphers) {
if (!ciphers.length) {
deferred.reject(); deferred.reject();
return; return;
} }
var sortedLogins = logins.sort(self.sortLoginsByLastUsed); var sortedCiphers = ciphers.sort(self.sortCiphersByLastUsed);
deferred.resolve(sortedLogins[0]); deferred.resolve(sortedCiphers[0]);
}); });
return deferred.promise; return deferred.promise;
}; };
LoginService.prototype.saveWithServer = function (login) { LoginService.prototype.saveWithServer = function (cipher) {
var deferred = Q.defer(); var deferred = Q.defer();
var self = this, var self = this,
request = new CipherRequest(login, 1); // 1 = Login // TODO
request = new CipherRequest(cipher, 1); // 1 = Login
if (!login.id) { if (!cipher.id) {
self.apiService.postCipher(request, apiSuccess, function (response) { self.apiService.postCipher(request).then(apiSuccess, function (response) {
handleError(response, deferred); deferred.reject(response);
}); });
} }
else { else {
self.apiService.putCipher(login.id, request, apiSuccess, function (response) { self.apiService.putCipher(cipher.id, request).then(apiSuccess, function (response) {
handleError(response, deferred); deferred.reject(response);
}); });
} }
function apiSuccess(response) { function apiSuccess(response) {
login.id = response.id; cipher.id = response.id;
self.userService.getUserId(function (userId) { self.userService.getUserIdPromise().then(function (userId) {
var data = new LoginData(response, userId); var data = new LoginData(response, userId);
self.upsert(data, function () { return self.upsert(data);
deferred.resolve(login); }).then(function () {
}); deferred.resolve(cipher);
}); });
} }
return deferred.promise; return deferred.promise;
}; };
LoginService.prototype.upsert = function (login, callback) { LoginService.prototype.upsert = function (cipher) {
if (!callback || typeof callback !== 'function') { var self = this,
throw 'callback function required'; key = null;
}
var self = this; return self.userService.getUserIdPromise().then(function (userId) {
key = 'ciphers_' + userId;
return self.utilsService.getObjFromStorage(key);
}).then(function (ciphers) {
if (!ciphers) {
ciphers = {};
}
self.userService.getUserId(function (userId) { if (cipher.constructor === Array) {
var loginsKey = 'sites_' + userId; for (var i = 0; i < cipher.length; i++) {
ciphers[cipher[i].id] = cipher[i];
chrome.storage.local.get(loginsKey, function (obj) {
var logins = obj[loginsKey];
if (!logins) {
logins = {};
} }
}
else {
ciphers[cipher.id] = cipher;
}
if (login.constructor === Array) { return self.utilsService.saveObjToStorage(key, ciphers);
for (var i = 0; i < login.length; i++) { }).then(function () {
logins[login[i].id] = login[i]; self.decryptedCipherCache = null;
}
}
else {
logins[login.id] = login;
}
obj[loginsKey] = logins;
chrome.storage.local.set(obj, function () {
self.decryptedLoginCache = null;
callback();
});
});
}); });
}; };
LoginService.prototype.updateLastUsedDate = function (id, callback) { LoginService.prototype.updateLastUsedDate = function (id) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this; var self = this;
var localDataKey = 'sitesLocalData';
chrome.storage.local.get(localDataKey, function (obj) { var ciphersLocalData = null;
var loginsLocalData = obj[localDataKey]; return self.utilsService.getObjFromStorage(self.localDataKey).then(function (obj) {
if (!loginsLocalData) { ciphersLocalData = obj;
loginsLocalData = {};
if (!ciphersLocalData) {
ciphersLocalData = {};
} }
if (loginsLocalData[id]) { if (ciphersLocalData[id]) {
loginsLocalData[id].lastUsedDate = new Date().getTime(); ciphersLocalData[id].lastUsedDate = new Date().getTime();
} }
else { else {
loginsLocalData[id] = { ciphersLocalData[id] = {
lastUsedDate: new Date().getTime() lastUsedDate: new Date().getTime()
}; };
} }
obj[localDataKey] = loginsLocalData; return self.utilsService.saveObjToStorage(key, ciphersLocalData);
}).then(function () {
if (!self.decryptedCipherCache) {
return;
}
chrome.storage.local.set(obj, function () { for (var i = 0; i < self.decryptedCipherCache.length; i++) {
if (self.decryptedLoginCache) { if (self.decryptedCipherCache[i].id === id) {
for (var i = 0; i < self.decryptedLoginCache.length; i++) { self.decryptedCipherCache[i].localData = ciphersLocalData[id];
if (self.decryptedLoginCache[i].id === id) { break;
self.decryptedLoginCache[i].localData = loginsLocalData[id]; }
break; }
} });
};
LoginService.prototype.replace = function (ciphers) {
var self = this;
self.userService.getUserIdPromise().then(function (userId) {
return self.utilsService.saveObjToStorage('ciphers_' + userId, ciphers);
}).then(function () {
self.decryptedCipherCache = null;
});
};
LoginService.prototype.clear = function (userId) {
var self = this;
return self.utilsService.removeFromStorage('ciphers_' + userId).then(function () {
self.decryptedCipherCache = null;
});
};
LoginService.prototype.delete = function (id) {
var self = this,
key = null;
self.userService.getUserIdPromise().then(function () {
key = 'ciphers_' + userId;
return self.utilsService.getObjFromStorage(key);
}).then(function (logins) {
if (!logins) {
return null;
}
if (id.constructor === Array) {
for (var i = 0; i < id.length; i++) {
if (id[i] in logins) {
delete logins[id[i]];
} }
} }
}
else if (id in logins) {
delete logins[id];
}
else {
return null;
}
callback(); return logins;
}); }).then(function (logins) {
}); if (!logins) {
}; return false;
}
LoginService.prototype.replace = function (logins, callback) { return self.utilsService.saveObjToStorage(key, logins);
if (!callback || typeof callback !== 'function') { }).then(function (clearCache) {
throw 'callback function required'; if (clearCache !== false) {
} self.decryptedCipherCache = null;
}
var self = this;
self.userService.getUserId(function (userId) {
var obj = {};
obj['sites_' + userId] = logins;
chrome.storage.local.set(obj, function () {
self.decryptedLoginCache = null;
callback();
});
});
};
LoginService.prototype.clear = function (userId, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
chrome.storage.local.remove('sites_' + userId, function () {
self.decryptedLoginCache = null;
callback();
});
};
LoginService.prototype.delete = function (id, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
self.userService.getUserId(function (userId) {
var loginsKey = 'sites_' + userId;
chrome.storage.local.get(loginsKey, function (obj) {
var logins = obj[loginsKey];
if (!logins) {
callback();
return;
}
if (id.constructor === Array) {
for (var i = 0; i < id.length; i++) {
if (id[i] in logins) {
delete logins[id[i]];
}
}
}
else if (id in logins) {
delete logins[id];
}
else {
callback();
return;
}
obj[loginsKey] = logins;
chrome.storage.local.set(obj, function () {
self.decryptedLoginCache = null;
callback();
});
});
}); });
}; };
LoginService.prototype.deleteWithServer = function (id) { LoginService.prototype.deleteWithServer = function (id) {
var deferred = Q.defer();
var self = this; var self = this;
self.apiService.deleteCipher(id, function () { return self.apiService.deleteCipher(id).then(function () {
self.delete(id, function () { return self.delete(id);
deferred.resolve();
});
}, function (response) {
handleError(response, deferred);
}); });
return deferred.promise;
}; };
LoginService.prototype.saveNeverDomain = function (domain) { LoginService.prototype.saveNeverDomain = function (domain) {
var deferred = Q.defer();
var neverKey = 'neverDomains';
if (!domain) { if (!domain) {
deferred.resolve(); return Q.fcall(function () { });
}
else {
chrome.storage.local.get(neverKey, function (obj) {
var domains = obj[neverKey];
if (!domains) {
domains = {};
}
domains[domain] = null;
obj[neverKey] = domains;
chrome.storage.local.set(obj, function () {
deferred.resolve();
});
});
} }
return deferred.promise; var key = 'neverDomains';
return self.utilsService.getObjFromStorage(key).then(function (domains) {
if (!domains) {
domains = {};
}
domains[domain] = null;
return self.utilsService.saveObjToStorage(key, domains);
});
}; };
LoginService.prototype.saveAttachmentWithServer = function (login, unencryptedFile) { LoginService.prototype.saveAttachmentWithServer = function (cipher, unencryptedFile) {
var deferred = Q.defer(); var deferred = Q.defer(),
var self = this; self = this,
response = null,
data = null,
apiErrored = false;
var key, encFileName; var key, encFileName;
var reader = new FileReader(); var reader = new FileReader();
reader.readAsArrayBuffer(unencryptedFile); reader.readAsArrayBuffer(unencryptedFile);
reader.onload = function (evt) { reader.onload = function (evt) {
self.cryptoService.getOrgKey(login.organizationId).then(function (theKey) { self.cryptoService.getOrgKey(cipher.organizationId).then(function (theKey) {
key = theKey; key = theKey;
return self.cryptoService.encrypt(unencryptedFile.name, key); return self.cryptoService.encrypt(unencryptedFile.name, key);
}).then(function (fileName) { }).then(function (fileName) {
@@ -491,18 +434,24 @@ function initLoginService() {
var blob = new Blob([encData], { type: 'application/octet-stream' }); var blob = new Blob([encData], { type: 'application/octet-stream' });
fd.append('data', blob, encFileName.encryptedString); fd.append('data', blob, encFileName.encryptedString);
self.apiService.postCipherAttachment(login.id, fd, return self.apiService.postCipherAttachment(cipher.id, fd);
function (response) { }).then(function (resp) {
self.userService.getUserId(function (userId) { response = resp;
var data = new LoginData(response, userId); return self.userService.getUserIdPromise();
self.upsert(data, function () { }, function (resp) {
deferred.resolve(new Login(data)); apiErrored = true;
}); handleErrorMessage(resp, deferred);
}); }).then(function (userId) {
}, if (apiErrored === true) {
function (response) { return;
handleErrorMessage(response, deferred); }
});
data = new LoginData(response, userId);
return self.upsert(data);
}).then(function () {
if (data) {
deferred.resolve(new Login(data));
}
}); });
}; };
reader.onerror = function (evt) { reader.onerror = function (evt) {
@@ -512,48 +461,46 @@ function initLoginService() {
return deferred.promise; return deferred.promise;
}; };
LoginService.prototype.deleteAttachment = function (id, attachmentId, callback) { LoginService.prototype.deleteAttachment = function (id, attachmentId) {
if (!callback || typeof callback !== 'function') { var self = this,
throw 'callback function required'; key = null;
}
var self = this; self.userService.getUserIdPromise().then(function () {
key = 'ciphers_' + userId;
self.userService.getUserId(function (userId) { return self.utilsService.getObjFromStorage(key);
var loginsKey = 'sites_' + userId; }).then(function (logins) {
if (logins && id in logins && logins[id].attachments) {
chrome.storage.local.get(loginsKey, function (obj) { for (var i = 0; i < logins[id].attachments.length; i++) {
var logins = obj[loginsKey]; if (logins[id].attachments[i].id === attachmentId) {
if (logins && id in logins && logins[id].attachments) { logins[id].attachments.splice(i, 1);
for (var i = 0; i < logins[id].attachments.length; i++) {
if (logins[id].attachments[i].id === attachmentId) {
logins[id].attachments.splice(i, 1);
}
} }
}
obj[loginsKey] = logins; return self.utilsService.saveObjToStorage(key, logins);
chrome.storage.local.set(obj, function () { }
self.decryptedLoginCache = null; else {
callback(); return false;
}); }
} }).then(function (clearCache) {
else { if (clearCache !== false) {
callback(); self.decryptedCipherCache = null;
} }
});
}); });
}; };
LoginService.prototype.deleteAttachmentWithServer = function (id, attachmentId) { LoginService.prototype.deleteAttachmentWithServer = function (id, attachmentId) {
var deferred = Q.defer(); var self = this,
deferred = Q.defer();
var self = this; self.apiService.deleteCipherAttachment(id, attachmentId).then(function () {
self.apiService.deleteCipherAttachment(id, attachmentId, function () { return self.deleteAttachment(id, attachmentId);
self.deleteAttachment(id, attachmentId, function () {
deferred.resolve();
});
}, function (response) { }, function (response) {
handleErrorMessage(response, deferred); handleErrorMessage(response, deferred);
return false;
}).then(function (apiSuccess) {
if (apiSuccess !== false) {
deferred.resolve();
}
}); });
return deferred.promise; return deferred.promise;

View File

@@ -1,5 +1,6 @@
function SettingsService(userService) { function SettingsService(userService, utilsService) {
this.userService = userService; this.userService = userService;
this.utilsService = utilsService;
this.settingsCache = null; this.settingsCache = null;
initSettingsService(); initSettingsService();
@@ -85,18 +86,10 @@ function initSettingsService() {
}); });
} }
SettingsService.prototype.clear = function (callback) { SettingsService.prototype.clear = function (userId) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this; var self = this;
return self.utilsService.removeFromStorage('settings_' + userId).then(function () {
this.userService.getUserId(function (userId) { self.settingsCache = null;
chrome.storage.local.remove('settings_' + userId, function () {
self.settingsCache = null;
callback();
});
}); });
}; };

View File

@@ -131,38 +131,22 @@ function initSyncService() {
} }
function syncFolders(self, userId, response) { function syncFolders(self, userId, response) {
var deferred = Q.defer();
var folders = {}; var folders = {};
for (var i = 0; i < response.length; i++) { for (var i = 0; i < response.length; i++) {
folders[response[i].id] = new FolderData(response[i], userId); folders[response[i].id] = new FolderData(response[i], userId);
} }
return self.folderService.replace(folders);
self.folderService.replace(folders, function () {
deferred.resolve();
});
return deferred.promise;
} }
function syncCiphers(self, userId, response) { function syncCiphers(self, userId, response) {
var deferred = Q.defer();
var logins = {}; var logins = {};
for (var i = 0; i < response.length; i++) { for (var i = 0; i < response.length; i++) {
var data = response[i]; var data = response[i];
if (data.type === 1) { if (data.type === 1) {
logins[data.id] = new LoginData(data, userId); logins[data.id] = new LoginData(data, userId);
} }
} }
return self.loginService.replace(logins);
self.loginService.replace(logins, function () {
deferred.resolve();
});
return deferred.promise;
} }
function syncSettings(self, userId, response) { function syncSettings(self, userId, response) {

View File

@@ -1,4 +1,5 @@
function TokenService() { function TokenService(utilsService) {
this.utilsService = utilsService;
initTokenService(); initTokenService();
} }
@@ -123,16 +124,13 @@ function initTokenService() {
}); });
}; };
TokenService.prototype.clearToken = function (callback) { TokenService.prototype.clearToken = function () {
if (!callback || typeof callback !== 'function') { var self = this;
throw 'callback function required'; return Q.all([
} self.utilsService.removeFromStorage('accessToken'),
self.utilsService.removeFromStorage('refreshToken')
_token = _decodedToken = _refreshToken = null; ]).then(function () {
chrome.storage.local.remove('accessToken', function () { _token = _decodedToken = _refreshToken = null;
chrome.storage.local.remove('refreshToken', function () {
callback();
});
}); });
}; };

View File

@@ -1,7 +1,8 @@
function UserService(tokenService, apiService, cryptoService) { function UserService(tokenService, apiService, cryptoService, utilsService) {
this.tokenService = tokenService; this.tokenService = tokenService;
this.apiService = apiService; this.apiService = apiService;
this.cryptoService = cryptoService; this.cryptoService = cryptoService;
this.utilsService = utilsService;
initUserService(); initUserService();
} }
@@ -72,6 +73,21 @@ function initUserService() {
}); });
}; };
UserService.prototype.getUserIdPromise = function () {
if (_userId) {
return Q.fcall(function () {
return _userId;
});
}
return utilsService.getObjFromStorage(userIdKey).then(function (obj) {
if (obj) {
_userId = obj;
}
return _userId;
});
};
UserService.prototype.getEmail = function (callback) { UserService.prototype.getEmail = function (callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
throw 'callback function required'; throw 'callback function required';
@@ -108,18 +124,14 @@ function initUserService() {
return deferred.promise; return deferred.promise;
}; };
UserService.prototype.clear = function (callback) { UserService.prototype.clear = function () {
if (!callback || typeof callback !== 'function') { var self = this;
throw 'callback function required'; return Q.all([
} self.utilsService.removeFromStorage(userIdKey),
self.utilsService.removeFromStorage(userEmailKey),
_userId = _email = _stamp = null; self.utilsService.removeFromStorage(stampKey)
chrome.storage.local.remove(userIdKey, function () { ]).then(function () {
chrome.storage.local.remove(userEmailKey, function () { _userId = _email = _stamp = null;
chrome.storage.local.remove(stampKey, function () {
callback();
});
});
}); });
}; };

View File

@@ -245,6 +245,37 @@ function initUtilsService() {
theWindow.location.search.indexOf('uilocation=popup') > -1; theWindow.location.search.indexOf('uilocation=popup') > -1;
}; };
UtilsService.prototype.saveObjToStorage = function (key, obj) {
var deferred = Q.defer();
var storedObj = {};
storedObj[key] = obj;
chrome.storage.local.set(storedObj, function () {
deferred.resolve();
});
return deferred.promise;
};
UtilsService.prototype.removeFromStorage = function (key) {
var deferred = Q.defer();
chrome.storage.local.remove(key, function () {
deferred.resolve();
});
return deferred.promise;
};
UtilsService.prototype.getObjFromStorage = function (key) {
var deferred = Q.defer();
chrome.storage.local.get(key, function (obj) {
if (obj && obj[key]) {
deferred.resolve(obj[key]);
}
else {
deferred.resolve(null);
}
});
return deferred.promise;
};
function validIpAddress(ipString) { function validIpAddress(ipString) {
var ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; var ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ipString); return ipRegex.test(ipString);