Merge remote-tracking branch 'bitwarden/master'

This commit is contained in:
Felix 2017-02-09 10:05:13 +08:00
commit 54ba0cdce3
31 changed files with 2024 additions and 668 deletions

View File

@ -9,7 +9,8 @@
merge = require('merge-stream'),
browserify = require('browserify'),
source = require('vinyl-source-stream'),
googleWebFonts = require('gulp-google-webfonts');
googleWebFonts = require('gulp-google-webfonts'),
webpack = require('webpack-stream');
var paths = {};
paths.dist = './dist/';
@ -29,7 +30,7 @@ gulp.task('lint', function () {
gulp.task('build', function (cb) {
return runSequence(
'clean',
['browserify', 'lib', 'less', 'lint', 'webfonts'],
['browserify', 'webpack', 'lib', 'less', 'lint', 'webfonts'],
cb);
});
@ -91,10 +92,6 @@ gulp.task('lib', ['clean:lib'], function () {
paths.npmDir + 'angular-toastr/dist/angular-toastr.css'],
dest: paths.libDir + 'angular-toastr'
},
{
src: [paths.npmDir + 'sjcl/core/cbc.js', paths.npmDir + 'sjcl/core/bitArray.js', paths.npmDir + 'sjcl/sjcl.js'],
dest: paths.libDir + 'sjcl'
},
{
src: paths.npmDir + 'ngclipboard/dist/ngclipboard.js',
dest: paths.libDir + 'ngclipboard'
@ -135,13 +132,41 @@ gulp.task('lib', ['clean:lib'], function () {
return merge(tasks);
});
gulp.task('browserify', function () {
gulp.task('browserify', ['browserify:tldjs']);
gulp.task('browserify:tldjs', function () {
return browserify(paths.npmDir + 'tldjs/index.js', { standalone: 'tldjs' })
.bundle()
.pipe(source('tld.js'))
.pipe(gulp.dest(paths.libDir + 'tldjs'));
});
gulp.task('webpack', ['webpack:forge']);
gulp.task('webpack:forge', function () {
var forgeDir = paths.npmDir + '/node-forge/lib/';
return gulp.src([
forgeDir + 'pbkdf2.js',
forgeDir + 'aes.js',
forgeDir + 'hmac.js',
forgeDir + 'sha256.js',
forgeDir + 'random.js',
forgeDir + 'forge.js'
]).pipe(webpack({
output: {
filename: 'forge.js',
library: 'forge',
libraryTarget: 'umd'
},
node: {
Buffer: false,
process: false,
crypto: false,
setImmediate: false
}
})).pipe(gulp.dest(paths.libDir + 'forge'));
});
gulp.task('less', function () {
return gulp.src(paths.lessDir + 'popup.less')

View File

@ -2,7 +2,6 @@
"name": "bitwarden",
"version": "0.0.0",
"devDependencies": {
"sjcl": "1.0.6",
"jquery": "2.2.4",
"bootstrap": "3.3.7",
"font-awesome": "4.7.0",
@ -33,6 +32,8 @@
"browserify": "13.1.1",
"vinyl-source-stream": "1.1.0",
"gulp-google-webfonts": "0.0.14",
"ng-infinite-scroll": "1.3.0"
"ng-infinite-scroll": "1.3.0",
"node-forge": "0.7.0",
"webpack-stream": "3.2.0"
}
}

View File

@ -32,7 +32,7 @@
"description": "Close"
},
"submit": {
"message": "Lähetä",
"message": "Jatka",
"description": "Submit"
},
"emailAddress": {
@ -600,7 +600,7 @@
"description": "Are you sure you want to overwrite the current password?"
},
"lost2FAApp": {
"message": "Ei pääsyä tunnistautumispalveluun?",
"message": "Etkö pääse tunnistautumispalveluun?",
"description": "Lost authenticator app?"
},
"credits": {

View File

@ -5,14 +5,15 @@ var constantsService = new ConstantsService();
var utilsService = new UtilsService();
var cryptoService = new CryptoService(constantsService);
var tokenService = new TokenService();
var apiService = new ApiService(tokenService);
var appIdService = new AppIdService();
var apiService = new ApiService(tokenService, appIdService, utilsService, logout);
var userService = new UserService(tokenService, apiService, cryptoService);
var loginService = new LoginService(cryptoService, userService, apiService);
var settingsService = new SettingsService(userService);
var loginService = new LoginService(cryptoService, userService, apiService, settingsService);
var folderService = new FolderService(cryptoService, userService, apiService);
var syncService = new SyncService(loginService, folderService, userService, apiService);
var syncService = new SyncService(loginService, folderService, userService, apiService, settingsService);
var autofillService = new AutofillService();
var passwordGenerationService = new PasswordGenerationService();
var appIdService = new AppIdService();
chrome.commands.onCommand.addListener(function (command) {
if (command === 'generate_password') {
@ -27,27 +28,20 @@ chrome.commands.onCommand.addListener(function (command) {
}
});
var loadMenuRan = false,
loginToAutoFill = null,
var loginToAutoFill = null,
pageDetailsToAutoFill = [],
autofillTimeout = null;
autofillTimeout = null,
menuOptionsLoaded = [];
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.command === 'loggedOut' || msg.command === 'loggedIn' || msg.command === 'unlocked' || msg.command === 'locked') {
if (loadMenuRan) {
return;
}
loadMenuRan = true;
if (msg.command === 'loggedIn' || msg.command === 'unlocked' || msg.command === 'locked') {
setIcon();
refreshBadgeAndMenu();
}
else if (msg.command === 'logout') {
logout(msg.expired, function () { });
}
else if (msg.command === 'syncCompleted' && msg.successfully) {
if (loadMenuRan) {
return;
}
loadMenuRan = true;
setTimeout(refreshBadgeAndMenu, 2000);
}
else if (msg.command === 'bgOpenOverlayPopup') {
@ -75,7 +69,6 @@ chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
saveAddLogin(sender.tab);
}
else if (msg.command === 'collectPageDetailsResponse') {
// messageCurrentTab('openNotificationBar', { type: 'add', typeData: null });
if (msg.contentScript) {
var forms = autofillService.getFormsWithPasswordFields(msg.details);
messageTab(msg.tabId, 'pageDetails', { details: msg.details, forms: forms });
@ -123,7 +116,13 @@ if (chrome.runtime.onInstalled) {
});
}
var buildingContextMenu = false;
function buildContextMenu(callback) {
if (buildingContextMenu) {
return;
}
buildingContextMenu = true;
chrome.contextMenus.removeAll(function () {
chrome.contextMenus.create({
type: 'normal',
@ -138,6 +137,15 @@ function buildContextMenu(callback) {
contexts: ['all'],
title: i18nService.autoFill
}, function () {
if (utilsService.isFirefox()) {
// Firefox does not support writing to the clipboard from background
buildingContextMenu = false;
if (callback) {
callback();
}
return;
}
chrome.contextMenus.create({
type: 'normal',
id: 'copy-username',
@ -164,6 +172,7 @@ function buildContextMenu(callback) {
contexts: ['all'],
title: i18nService.generatePasswordCopied
}, function () {
buildingContextMenu = false;
if (callback) {
callback();
}
@ -176,7 +185,6 @@ function buildContextMenu(callback) {
}
chrome.tabs.onActivated.addListener(function (activeInfo) {
checkLoginsToAdd();
refreshBadgeAndMenu();
});
@ -200,8 +208,16 @@ chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
refreshBadgeAndMenu();
});
chrome.windows.onFocusChanged.addListener(function (windowId) {
if (windowId === null || windowId < 0) {
return;
}
refreshBadgeAndMenu();
});
function refreshBadgeAndMenu() {
chrome.tabs.query({ active: true }, function (tabs) {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function (tabs) {
var tab = null;
if (tabs.length > 0) {
tab = tabs[0];
@ -213,7 +229,7 @@ function refreshBadgeAndMenu() {
buildContextMenu(function () {
loadMenuAndUpdateBadge(tab.url, tab.id, true);
onUpdatedRan = onReplacedRan = loadMenuRan = false;
onUpdatedRan = onReplacedRan = false;
});
});
}
@ -230,10 +246,12 @@ function loadMenuAndUpdateBadge(url, tabId, loadContextMenuOptions) {
chrome.browserAction.setBadgeBackgroundColor({ color: '#294e5f' });
menuOptionsLoaded = [];
loginService.getAllDecryptedForDomain(tabDomain).then(function (logins) {
sortLogins(logins);
for (var i = 0; i < logins.length; i++) {
if (loadContextMenuOptions) {
if (loadContextMenuOptions) {
for (var i = 0; i < logins.length; i++) {
loadLoginContextMenuOptions(logins[i]);
}
}
@ -331,7 +349,7 @@ function messageCurrentTab(command, data) {
});
}
function messageTab(tabId, command, data) {
function messageTab(tabId, command, data, callback) {
if (!tabId) {
return;
}
@ -344,11 +362,16 @@ function messageTab(tabId, command, data) {
obj['data'] = data;
}
chrome.tabs.sendMessage(tabId, obj);
chrome.tabs.sendMessage(tabId, obj, function () {
if (callback) {
callback();
}
});
}
function collectPageDetailsForContentScript(tab) {
chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tabId: tab.id, contentScript: true }, function () { });
chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tabId: tab.id, contentScript: true }, function () {
});
}
function addLogin(login, tab) {
@ -374,6 +397,7 @@ function addLogin(login, tab) {
username: login.username,
password: login.password,
name: loginDomain,
domain: loginDomain,
uri: login.url,
tabId: tab.id,
expires: new Date((new Date()).getTime() + 30 * 60000) // 30 minutes
@ -387,7 +411,7 @@ cleanupLoginsToAdd();
setInterval(cleanupLoginsToAdd, 2 * 60 * 1000); // check every 2 minutes
function cleanupLoginsToAdd() {
var now = new Date();
for (var i = loginsToAdd.length - 1; i >= 0 ; i--) {
for (var i = loginsToAdd.length - 1; i >= 0; i--) {
if (loginsToAdd[i].expires < now) {
loginsToAdd.splice(i, 1);
}
@ -395,7 +419,7 @@ function cleanupLoginsToAdd() {
}
function removeAddLogin(tab) {
for (var i = loginsToAdd.length - 1; i >= 0 ; i--) {
for (var i = loginsToAdd.length - 1; i >= 0; i--) {
if (loginsToAdd[i].tabId === tab.id) {
loginsToAdd.splice(i, 1);
}
@ -403,35 +427,42 @@ function removeAddLogin(tab) {
}
function saveAddLogin(tab) {
for (var i = loginsToAdd.length - 1; i >= 0 ; i--) {
for (var i = loginsToAdd.length - 1; i >= 0; i--) {
if (loginsToAdd[i].tabId === tab.id) {
var loginToAdd = loginsToAdd[i];
loginsToAdd.splice(i, 1);
loginService.encrypt({
id: null,
folderId: null,
favorite: false,
name: loginToAdd.name,
uri: loginToAdd.uri,
username: loginToAdd.username,
password: loginToAdd.password,
notes: null
}).then(function (loginModel) {
var login = new Login(loginModel, true);
loginService.saveWithServer(login).then(function (login) {
ga('send', {
hitType: 'event',
eventAction: 'Added Login from Notification Bar'
var tabDomain = tldjs.getDomain(tab.url);
if (tabDomain && tabDomain === loginToAdd.domain) {
loginsToAdd.splice(i, 1);
loginService.encrypt({
id: null,
folderId: null,
favorite: false,
name: loginToAdd.name,
uri: loginToAdd.uri,
username: loginToAdd.username,
password: loginToAdd.password,
notes: null
}).then(function (loginModel) {
var login = new Login(loginModel, true);
loginService.saveWithServer(login).then(function (login) {
ga('send', {
hitType: 'event',
eventAction: 'Added Login from Notification Bar'
});
});
});
});
messageTab(tab.id, 'closeNotificationBar');
messageTab(tab.id, 'closeNotificationBar');
}
}
}
}
function checkLoginsToAdd(tab) {
function checkLoginsToAdd(tab, callback) {
if (!loginsToAdd.length) {
if (callback) {
callback();
}
return;
}
@ -449,18 +480,29 @@ function checkLoginsToAdd(tab) {
function check() {
if (!tab) {
if (callback) {
callback();
}
return;
}
var tabDomain = tldjs.getDomain(tab.url);
if (!tabDomain) {
if (callback) {
callback();
}
return;
}
for (var i = 0; i < loginsToAdd.length; i++) {
// loginsToAdd[x].name is the domain here
if (loginsToAdd[i].tabId === tab.id && loginsToAdd[i].name === tabDomain) {
messageTab(tab.id, 'openNotificationBar', { type: 'add' });
if (loginsToAdd[i].tabId === tab.id && loginsToAdd[i].domain === tabDomain) {
messageTab(tab.id, 'openNotificationBar', {
type: 'add'
}, function () {
if (callback) {
callback();
}
});
break;
}
}
@ -482,7 +524,8 @@ function startAutofillPage(login) {
return;
}
chrome.tabs.sendMessage(tabId, { command: 'collectPageDetails', tabId: tabId }, function () { });
chrome.tabs.sendMessage(tabId, { command: 'collectPageDetails', tabId: tabId }, function () {
});
});
}
@ -552,6 +595,11 @@ function loadNoLoginsContextMenuOptions(noLoginsMessage) {
}
function loadContextMenuOptions(title, idSuffix, login) {
if (menuOptionsLoaded.indexOf(idSuffix) > -1) {
return;
}
menuOptionsLoaded.push(idSuffix);
if (!login || (login.password && login.password !== '')) {
chrome.contextMenus.create({
type: 'normal',
@ -562,6 +610,11 @@ function loadContextMenuOptions(title, idSuffix, login) {
});
}
if (utilsService.isFirefox()) {
// Firefox does not support writing to the clipboard from background
return;
}
if (!login || (login.username && login.username !== '')) {
chrome.contextMenus.create({
type: 'normal',
@ -583,6 +636,34 @@ function loadContextMenuOptions(title, idSuffix, login) {
}
}
// TODO: Fix callback hell by moving to promises
function logout(expired, callback) {
userService.getUserId(function (userId) {
syncService.setLastSync(new Date(0), function () {
settingsService.clear(function () {
tokenService.clearToken(function () {
cryptoService.clearKey(function () {
cryptoService.clearKeyHash(function () {
userService.clearUserIdAndEmail(function () {
loginService.clear(userId, function () {
folderService.clear(userId, function () {
chrome.runtime.sendMessage({
command: 'doneLoggingOut', expired: expired
});
setIcon();
refreshBadgeAndMenu();
callback();
});
});
});
});
});
});
});
});
});
}
function copyToClipboard(text) {
if (window.clipboardData && window.clipboardData.setData) {
// IE specific code path to prevent textarea being shown while dialog is visible.
@ -617,10 +698,12 @@ setInterval(fullSync, 5 * 60 * 1000); // check every 5 minutes
var syncInternal = 6 * 60 * 60 * 1000; // 6 hours
function fullSync(override) {
override = override || false;
syncService.getLastSync(function (lastSync) {
var now = new Date();
if (override || !lastSync || (now - lastSync) >= syncInternal) {
syncService.fullSync(function () { });
syncService.fullSync(override || false, function () {
});
}
});
}

View File

@ -1,4 +1,4 @@
!(function() {
!(function () {
/*
1Password Extension
@ -28,55 +28,928 @@
SOFTWARE.
*/
// MODIFICATIONS: populate isFirefox. Remove isChrome and isSafari since they are not used.
/*
MODIFICATIONS FROM ORIGINAL
1. Populate isFirefox
2. Remove isChrome and isSafari since they are not used.
3. Unminify and format to meet Mozilla review requirements.
*/
function collect(document, undefined) {
var isFirefox = navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1;
document.elementsByOPID={};document.addEventListener('input',function(b){!1!==b.a&&'input'===b.target.tagName.toLowerCase()&&(b.target.dataset['com.agilebits.onepassword.userEdited']='yes')},!0);
function q(b,d){function f(a,e){var c=a[e];if('string'==typeof c)return c;c=a.getAttribute(e);return'string'==typeof c?c:null}function h(a,e){if(-1===['text','password'].indexOf(e.type.toLowerCase())||!(m.test(a.value)||m.test(a.htmlID)||m.test(a.htmlName)||m.test(a.placeholder)||m.test(a['label-tag'])||m.test(a['label-data'])||m.test(a['label-aria'])))return!1;if(!a.visible)return!0;if('password'==e.type.toLowerCase())return!1;var c=e.type;v(e,!0);return c!==e.type}function n(a){switch(p(a.type)){case 'checkbox':return a.checked?
'✓':'';case 'hidden':a=a.value;if(!a||'number'!=typeof a.length)return'';254<a.length&&(a=a.substr(0,254)+'...SNIPPED');return a;default:return a.value}}function l(a){return a.options?(a=Array.prototype.slice.call(a.options).map(function(a){var c=a.text,c=c?p(c).replace(/\\s/mg,'').replace(/[~`!@$%^&*()\\-_+=:;'\"\\[\\]|\\\\,<.>\\?]/mg,''):null;return[c?c:null,a.value]}),{options:a}):null}function r(a){var e;for(a=a.parentElement||a.parentNode;a&&'td'!=p(a.tagName);)a=a.parentElement||a.parentNode;if(!a||
void 0===a)return null;e=a.parentElement||a.parentNode;if('tr'!=e.tagName.toLowerCase())return null;e=e.previousElementSibling;if(!e||'tr'!=(e.tagName+'').toLowerCase()||e.cells&&a.cellIndex>=e.cells.length)return null;a=e.cells[a.cellIndex];a=a.textContent||a.innerText;return a=x(a)}function s(a){var e,c=[];if(a.labels&&a.labels.length&&0<a.labels.length)c=Array.prototype.slice.call(a.labels);else{a.id&&(c=c.concat(Array.prototype.slice.call(w(b,'label[for='+JSON.stringify(a.id)+']'))));if(a.name){e=
w(b,'label[for='+JSON.stringify(a.name)+']');for(var f=0;f<e.length;f++)-1===c.indexOf(e[f])&&c.push(e[f])}for(e=a;e&&e!=b;e=e.parentNode)'label'===p(e.tagName)&&-1===c.indexOf(e)&&c.push(e)}0===c.length&&(e=a.parentNode,'dd'===e.tagName.toLowerCase()&&null!==e.previousElementSibling&&'dt'===e.previousElementSibling.tagName.toLowerCase()&&c.push(e.previousElementSibling));return 0<c.length?c.map(function(a){return(a.textContent||a.innerText).replace(/^\\s+/,'').replace(/\\s+$/,'').replace('\\n','').replace(/\\s{2,}/,
' ')}).join(''):null}function g(a,e,c,b){void 0!==b&&b===c||null===c||void 0===c||(a[e]=c)}function p(a){return'string'===typeof a?a.toLowerCase():(''+a).toLowerCase()}function w(a,b){var c=[];try{c=a.querySelectorAll(b)}catch(f){}return c}var t=b.defaultView?b.defaultView:window,m=RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|(\\\\b|_|-)passe(\\\\b|_|-)|contraseña|senha|密码|adgangskode|hasło|wachtwoord)','i'),u=Array.prototype.slice.call(w(b,'form')).map(function(a,b){var c={},d='__form__'+
b;a.opid=d;c.opid=d;g(c,'htmlName',f(a,'name'));g(c,'htmlID',f(a,'id'));d=f(a,'action');d=new URL(d,window.location.href);g(c,'htmlAction',d?d.href:null);g(c,'htmlMethod',f(a,'method'));return c}),E=Array.prototype.slice.call(y(b)).map(function(a,e){var c={},d='__'+e,k=-1==a.maxLength?999:a.maxLength;if(!k||'number'===typeof k&&isNaN(k))k=999;b.elementsByOPID[d]=a;a.opid=d;c.opid=d;c.elementNumber=e;g(c,'maxLength',Math.min(k,999),999);c.visible=z(a);c.viewable=A(a);g(c,'htmlID',f(a,'id'));g(c,'htmlName',
f(a,'name'));g(c,'htmlClass',f(a,'class'));g(c,'tabindex',f(a,'tabindex'));g(c,'title',f(a,'title'));g(c,'userEdited',!!a.dataset['com.agilebits.onepassword.userEdited']);if('hidden'!=p(a.type)){g(c,'label-tag',s(a));g(c,'label-data',f(a,'data-label'));g(c,'label-aria',f(a,'aria-label'));g(c,'label-top',r(a));d=[];for(k=a;k&&k.nextSibling;){k=k.nextSibling;if(B(k))break;C(d,k)}g(c,'label-right',d.join(''));d=[];D(a,d);d=d.reverse().join('');g(c,'label-left',d);g(c,'placeholder',f(a,'placeholder'))}g(c,
'rel',f(a,'rel'));g(c,'type',p(f(a,'type')));g(c,'value',n(a));g(c,'checked',a.checked,!1);g(c,'autoCompleteType',a.getAttribute('x-autocompletetype')||a.getAttribute('autocompletetype')||a.getAttribute('autocomplete'),'off');g(c,'disabled',a.disabled);g(c,'readonly',a.b||a.readOnly);g(c,'selectInfo',l(a));g(c,'aria-hidden','true'==a.getAttribute('aria-hidden'),!1);g(c,'aria-disabled','true'==a.getAttribute('aria-disabled'),!1);g(c,'aria-haspopup','true'==a.getAttribute('aria-haspopup'),!1);g(c,'data-unmasked',
a.dataset.unmasked);g(c,'data-stripe',f(a,'data-stripe'));g(c,'onepasswordFieldType',a.dataset.onepasswordFieldType||a.type);g(c,'onepasswordDesignation',a.dataset.onepasswordDesignation);g(c,'onepasswordSignInUrl',a.dataset.onepasswordSignInUrl);g(c,'onepasswordSectionTitle',a.dataset.onepasswordSectionTitle);g(c,'onepasswordSectionFieldKind',a.dataset.onepasswordSectionFieldKind);g(c,'onepasswordSectionFieldTitle',a.dataset.onepasswordSectionFieldTitle);g(c,'onepasswordSectionFieldValue',a.dataset.onepasswordSectionFieldValue);
a.form&&(c.form=f(a.form,'opid'));g(c,'fakeTested',h(c,a),!1);return c});E.filter(function(a){return a.fakeTested}).forEach(function(a){var e=b.elementsByOPID[a.opid];e.getBoundingClientRect();var c=e.value;!e||e&&'function'!==typeof e.click||e.click();v(e,!1);e.dispatchEvent(F(e,'keydown'));e.dispatchEvent(F(e,'keypress'));e.dispatchEvent(F(e,'keyup'));e.value!==c&&(e.value=c);e.click&&e.click();a.postFakeTestVisible=z(e);a.postFakeTestViewable=A(e);a.postFakeTestType=e.type;a=e.value;var c=e.ownerDocument.createEvent('HTMLEvents'),
d=e.ownerDocument.createEvent('HTMLEvents');e.dispatchEvent(F(e,'keydown'));e.dispatchEvent(F(e,'keypress'));e.dispatchEvent(F(e,'keyup'));d.initEvent('input',!0,!0);e.dispatchEvent(d);c.initEvent('change',!0,!0);e.dispatchEvent(c);e.blur();e.value!==a&&(e.value=a)});t={documentUUID:d,title:b.title,url:t.location.href,documentUrl:b.location.href,tabUrl:t.location.href,forms:function(a){var b={};a.forEach(function(a){b[a.opid]=a});return b}(u),fields:E,collectedTimestamp:(new Date).getTime()};(u=document.querySelector('[data-onepassword-title]'))&&
u.dataset[DISPLAY_TITLE_ATTRIBUE]&&(t.displayTitle=u.dataset.onepasswordTitle);return t};document.elementForOPID=G;function F(b,d){var f;isFirefox?(f=document.createEvent('KeyboardEvent'),f.initKeyEvent(d,!0,!1,null,!1,!1,!1,!1,0,0)):(f=b.ownerDocument.createEvent('Events'),f.initEvent(d,!0,!1),f.charCode=0,f.keyCode=0,f.which=0,f.srcElement=b,f.target=b);return f}window.LOGIN_TITLES=[/^\\W*log\\W*[oi]n\\W*$/i,/log\\W*[oi]n (?:securely|now)/i,/^\\W*sign\\W*[oi]n\\W*$/i,'continue','submit','weiter','accès','вход','connexion','entrar','anmelden','accedi','valider','登录','लॉग इन करें','change password'];
window.LOGIN_RED_HERRING_TITLES=['already have an account','sign in with'];window.REGISTER_TITLES='register;sign up;signup;join;create my account;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';');window.SEARCH_TITLES='search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' ');window.FORGOT_PASSWORD_TITLES='forgot geändert vergessen hilfe changeemail español'.split(' ');
window.REMEMBER_ME_TITLES=['remember me','rememberme','keep me signed in'];window.BACK_TITLES=['back','назад'];function x(b){var d=null;b&&(d=b.replace(/^\\s+|\\s+$|\\r?\\n.*$/mg,''),d=0<d.length?d:null);return d}function C(b,d){var f;f='';3===d.nodeType?f=d.nodeValue:1===d.nodeType&&(f=d.textContent||d.innerText);(f=x(f))&&b.push(f)}
function B(b){var d;b&&void 0!==b?(d='select option input form textarea button table iframe body head script'.split(' '),b?(b=b?(b.tagName||'').toLowerCase():'',d=d.constructor==Array?0<=d.indexOf(b):b===d):d=!1):d=!0;return d}
function D(b,d,f){var h;for(f||(f=0);b&&b.previousSibling;){b=b.previousSibling;if(B(b))return;C(d,b)}if(b&&0===d.length){for(h=null;!h;){b=b.parentElement||b.parentNode;if(!b)return;for(h=b.previousSibling;h&&!B(h)&&h.lastChild;)h=h.lastChild}B(h)||(C(d,h),0===d.length&&D(h,d,f+1))}}
function z(b){var d=b;b=(b=b.ownerDocument)?b.defaultView:{};for(var f;d&&d!==document;){f=b.getComputedStyle?b.getComputedStyle(d,null):d.style;if(!f)return!0;if('none'===f.display||'hidden'==f.visibility)return!1;d=d.parentNode}return d===document}
function A(b){var d=b.ownerDocument.documentElement,f=b.getBoundingClientRect(),h=d.scrollWidth,n=d.scrollHeight,l=f.left-d.clientLeft,d=f.top-d.clientTop,r;if(!z(b)||!b.offsetParent||10>b.clientWidth||10>b.clientHeight)return!1;var s=b.getClientRects();if(0===s.length)return!1;for(var g=0;g<s.length;g++)if(r=s[g],r.left>h||0>r.right)return!1;if(0>l||l>h||0>d||d>n)return!1;for(f=b.ownerDocument.elementFromPoint(l+(f.right>window.innerWidth?(window.innerWidth-l)/2:f.width/2),d+(f.bottom>window.innerHeight?
(window.innerHeight-d)/2:f.height/2));f&&f!==b&&f!==document;){if(f.tagName&&'string'===typeof f.tagName&&'label'===f.tagName.toLowerCase()&&b.labels&&0<b.labels.length)return 0<=Array.prototype.slice.call(b.labels).indexOf(f);f=f.parentNode}return f===b}
function G(b){var d;if(void 0===b||null===b)return null;try{var f=Array.prototype.slice.call(y(document)),h=f.filter(function(d){return d.opid==b});if(0<h.length)d=h[0],1<h.length&&console.warn('More than one element found with opid '+b);else{var n=parseInt(b.split('__')[1],10);isNaN(n)||(d=f[n])}}catch(l){console.error('An unexpected error occurred: '+l)}finally{return d}};function y(b){var d=[];try{d=b.querySelectorAll('input, select, button')}catch(f){}return d}function v(b,d){if(d){var f=b.value;b.focus();b.value!==f&&(b.value=f)}else b.focus()};
return JSON.stringify(q(document, 'oneshotUUID'));
document.elementsByOPID = {};
document.addEventListener('input', function (canuf) {
false !== canuf.a && 'input' === canuf.target.tagName.toLowerCase() && (canuf.target.dataset['com.agilebits.onepassword.userEdited'] = 'yes');
}, true);
function getPageDetails(theDoc, oneShotId) {
// start helpers
// get the value of a dom element's attribute
function getElementAttrValue(el, attrName) {
var attrVal = el[attrName];
if ('string' == typeof attrVal) {
return attrVal;
}
attrVal = el.getAttribute(attrName);
return 'string' == typeof attrVal ? attrVal : null;
}
// has the element been fake tested?
function checkIfFakeTested(field, el) {
if (-1 === ['text', 'password'].indexOf(el.type.toLowerCase()) ||
!(passwordRegEx.test(field.value) ||
passwordRegEx.test(field.htmlID) || passwordRegEx.test(field.htmlName) ||
passwordRegEx.test(field.placeholder) || passwordRegEx.test(field['label-tag']) ||
passwordRegEx.test(field['label-data']) || passwordRegEx.test(field['label-aria']))) {
return false;
}
if (!field.visible) {
return true;
}
if ('password' == el.type.toLowerCase()) {
return false;
}
var elType = el.type;
focusElement(el, true);
return elType !== el.type;
}
// get the value of a dom element
function getElementValue(el) {
switch (toLowerString(el.type)) {
case 'checkbox':
return el.checked ? '✓' : '';
case 'hidden':
el = el.value;
if (!el || 'number' != typeof el.length) {
return '';
}
254 < el.length && (el = el.substr(0, 254) + '...SNIPPED');
return el;
default:
return el.value;
}
}
// get all the options for a "select" element
function getSelectElementOptions(el) {
if (!el.options) {
return null;
}
var options = Array.prototype.slice.call(el.options).map(function (option) {
var optionText = option.text ?
toLowerString(option.text).replace(/\\s/gm, '').replace(/[~`!@$%^&*()\\-_+=:;'\"\\[\\]|\\\\,<.>\\?]/gm, '') :
null;
return [optionText ? optionText : null, option.value];
})
return {
options: options
};
}
// get the top label
function getLabelTop(el) {
var parent;
for (el = el.parentElement || el.parentNode; el && 'td' != toLowerString(el.tagName) ;) {
el = el.parentElement || el.parentNode;
}
if (!el || void 0 === el) {
return null;
}
parent = el.parentElement || el.parentNode;
if ('tr' != parent.tagName.toLowerCase()) {
return null;
}
parent = parent.previousElementSibling;
if (!parent || 'tr' != (parent.tagName + '').toLowerCase() ||
parent.cells && el.cellIndex >= parent.cells.length) {
return null;
}
el = parent.cells[el.cellIndex];
var elText = el.textContent || el.innerText;
return elText = cleanText(elText);
}
// get all the tags for a given label
function getLabelTag(el) {
var docLabel,
theLabels = [];
if (el.labels && el.labels.length && 0 < el.labels.length) {
theLabels = Array.prototype.slice.call(el.labels);
} else {
if (el.id) {
theLabels = theLabels.concat(Array.prototype.slice.call(
queryDoc(theDoc, 'label[for=' + JSON.stringify(el.id) + ']')));
}
if (el.name) {
docLabel = queryDoc(theDoc, 'label[for=' + JSON.stringify(el.name) + ']');
for (var labelIndex = 0; labelIndex < docLabel.length; labelIndex++) {
if (-1 === theLabels.indexOf(docLabel[labelIndex])) {
theLabels.push(docLabel[labelIndex])
}
}
}
for (var theEl = el; theEl && theEl != theDoc; theEl = theEl.parentNode) {
if ('label' === toLowerString(theEl.tagName) && -1 === theLabels.indexOf(theEl)) {
theLabels.push(theEl);
}
}
}
if (0 === theLabels.length) {
theEl = el.parentNode;
if ('dd' === theEl.tagName.toLowerCase() && null !== theEl.previousElementSibling
&& 'dt' === theEl.previousElementSibling.tagName.toLowerCase()) {
theLabels.push(theEl.previousElementSibling);
}
}
if (0 > theLabels.length) {
return null;
}
return theLabels.map(function (l) {
return (l.textContent || l.innerText)
.replace(/^\\s+/, '').replace(/\\s+$/, '').replace('\\n', '').replace(/\\s{2,}/, ' ');
}).join('');
}
// add property and value to the object if there is a value
function addProp(obj, prop, val, d) {
if (0 !== d && d === val || null === val || void 0 === val) {
return;
}
obj[prop] = val;
}
// lowercase helper
function toLowerString(s) {
return 'string' === typeof s ? s.toLowerCase() : ('' + s).toLowerCase();
}
// query the document helper
function queryDoc(doc, query) {
var els = [];
try {
els = doc.querySelectorAll(query);
} catch (e) { }
return els;
}
// end helpers
var theView = theDoc.defaultView ? theDoc.defaultView : window,
passwordRegEx = RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|(\\\\b|_|-)passe(\\\\b|_|-)|contraseña|senha|密码|adgangskode|hasło|wachtwoord)', 'i');
// get all the docs
var theForms = Array.prototype.slice.call(queryDoc(theDoc, 'form')).map(function (formEl, elIndex) {
var op = {},
formOpId = '__form__' + elIndex;
formEl.opid = formOpId;
op.opid = formOpId;
addProp(op, 'htmlName', getElementAttrValue(formEl, 'name'));
addProp(op, 'htmlID', getElementAttrValue(formEl, 'id'));
formOpId = getElementAttrValue(formEl, 'action');
formOpId = new URL(formOpId, window.location.href);
addProp(op, 'htmlAction', formOpId ? formOpId.href : null);
addProp(op, 'htmlMethod', getElementAttrValue(formEl, 'method'));
return op;
});
// get all the form fields
var theFields = Array.prototype.slice.call(getFormElements(theDoc)).map(function (el, elIndex) {
var field = {},
opId = '__' + elIndex,
elMaxLen = -1 == el.maxLength ? 999 : el.maxLength;
if (!elMaxLen || 'number' === typeof elMaxLen && isNaN(elMaxLen)) {
elMaxLen = 999;
}
theDoc.elementsByOPID[opId] = el;
el.opid = opId;
field.opid = opId;
field.elementNumber = elIndex;
addProp(field, 'maxLength', Math.min(elMaxLen, 999), 999);
field.visible = isElementVisible(el);
field.viewable = isElementViewable(el);
addProp(field, 'htmlID', getElementAttrValue(el, 'id'));
addProp(field, 'htmlName', getElementAttrValue(el, 'name'));
addProp(field, 'htmlClass', getElementAttrValue(el, 'class'));
addProp(field, 'tabindex', getElementAttrValue(el, 'tabindex'));
addProp(field, 'title', getElementAttrValue(el, 'title'));
addProp(field, 'userEdited', !!el.dataset['com.agilebits.onepassword.userEdited']);
if ('hidden' != toLowerString(el.type)) {
addProp(field, 'label-tag', getLabelTag(el));
addProp(field, 'label-data', getElementAttrValue(el, 'data-label'));
addProp(field, 'label-aria', getElementAttrValue(el, 'aria-label'));
addProp(field, 'label-top', getLabelTop(el));
var labelArr = [];
for (var sib = el; sib && sib.nextSibling;) {
sib = sib.nextSibling;
if (isKnownTag(sib)) {
break;
}
checkNodeType(labelArr, sib);
}
addProp(field, 'label-right', labelArr.join(''));
labelArr = [];
shiftForLeftLabel(el, labelArr);
labelArr = labelArr.reverse().join('');
addProp(field, 'label-left', labelArr);
addProp(field, 'placeholder', getElementAttrValue(el, 'placeholder'));
}
addProp(field, 'rel', getElementAttrValue(el, 'rel'));
addProp(field, 'type', toLowerString(getElementAttrValue(el, 'type')));
addProp(field, 'value', getElementValue(el));
addProp(field, 'checked', el.checked, false);
addProp(field, 'autoCompleteType', el.getAttribute('x-autocompletetype') || el.getAttribute('autocompletetype') || el.getAttribute('autocomplete'), 'off');
addProp(field, 'disabled', el.disabled);
addProp(field, 'readonly', el.b || el.readOnly);
addProp(field, 'selectInfo', getSelectElementOptions(el));
addProp(field, 'aria-hidden', 'true' == el.getAttribute('aria-hidden'), false);
addProp(field, 'aria-disabled', 'true' == el.getAttribute('aria-disabled'), false);
addProp(field, 'aria-haspopup', 'true' == el.getAttribute('aria-haspopup'), false);
addProp(field, 'data-unmasked', el.dataset.unmasked);
addProp(field, 'data-stripe', getElementAttrValue(el, 'data-stripe'));
addProp(field, 'onepasswordFieldType', el.dataset.onepasswordFieldType || el.type);
addProp(field, 'onepasswordDesignation', el.dataset.onepasswordDesignation);
addProp(field, 'onepasswordSignInUrl', el.dataset.onepasswordSignInUrl);
addProp(field, 'onepasswordSectionTitle', el.dataset.onepasswordSectionTitle);
addProp(field, 'onepasswordSectionFieldKind', el.dataset.onepasswordSectionFieldKind);
addProp(field, 'onepasswordSectionFieldTitle', el.dataset.onepasswordSectionFieldTitle);
addProp(field, 'onepasswordSectionFieldValue', el.dataset.onepasswordSectionFieldValue);
if (el.form) {
field.form = getElementAttrValue(el.form, 'opid');
}
addProp(field, 'fakeTested', checkIfFakeTested(field, el), false);
return field;
});
// test form fields
theFields.filter(function (f) {
return f.fakeTested;
}).forEach(function (f) {
var el = theDoc.elementsByOPID[f.opid];
el.getBoundingClientRect();
var originalValue = el.value;
// click it
!el || el && 'function' !== typeof el.click || el.click();
focusElement(el, false);
el.dispatchEvent(doEventOnElement(el, 'keydown'));
el.dispatchEvent(doEventOnElement(el, 'keypress'));
el.dispatchEvent(doEventOnElement(el, 'keyup'));
el.value !== originalValue && (el.value = originalValue);
el.click && el.click();
f.postFakeTestVisible = isElementVisible(el);
f.postFakeTestViewable = isElementViewable(el);
f.postFakeTestType = el.type;
var elValue = el.value;
var event1 = el.ownerDocument.createEvent('HTMLEvents'),
event2 = el.ownerDocument.createEvent('HTMLEvents');
el.dispatchEvent(doEventOnElement(el, 'keydown'));
el.dispatchEvent(doEventOnElement(el, 'keypress'));
el.dispatchEvent(doEventOnElement(el, 'keyup'));
event2.initEvent('input', true, true);
el.dispatchEvent(event2);
event1.initEvent('change', true, true);
el.dispatchEvent(event1);
el.blur();
el.value !== elValue && (el.value = elValue);
});
// build out the page details object. this is the final result
var pageDetails = {
documentUUID: oneShotId,
title: theDoc.title,
url: theView.location.href,
documentUrl: theDoc.location.href,
tabUrl: theView.location.href,
forms: function (forms) {
var formObj = {};
forms.forEach(function (f) {
formObj[f.opid] = f;
});
return formObj;
}(theForms),
fields: theFields,
collectedTimestamp: new Date().getTime()
};
// get proper page title. maybe they are using the special meta tag?
var theTitle = document.querySelector('[data-onepassword-title]')
if (theTitle && theTitle.dataset[DISPLAY_TITLE_ATTRIBUE]) {
pageDetails.displayTitle = theTitle.dataset.onepasswordTitle;
}
return pageDetails;
}
document.elementForOPID = getElementForOPID;
function doEventOnElement(kedol, fonor) {
var quebo;
isFirefox ? (quebo = document.createEvent('KeyboardEvent'), quebo.initKeyEvent(fonor, true, false, null, false, false, false, false, 0, 0)) : (quebo = kedol.ownerDocument.createEvent('Events'),
quebo.initEvent(fonor, true, false), quebo.charCode = 0, quebo.keyCode = 0, quebo.which = 0,
quebo.srcElement = kedol, quebo.target = kedol);
return quebo;
}
// some useful globals
window.LOGIN_TITLES = [/^\\W*log\\W*[oi]n\\W*$/i, /log\\W*[oi]n (?:securely|now)/i, /^\\W*sign\\W*[oi]n\\W*$/i, 'continue', 'submit', 'weiter', 'accès', 'вход', 'connexion', 'entrar', 'anmelden', 'accedi', 'valider', '登录', 'लॉग इन करें', 'change password'];
window.LOGIN_RED_HERRING_TITLES = ['already have an account', 'sign in with'];
window.REGISTER_TITLES = 'register;sign up;signup;join;create my account;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';');
window.SEARCH_TITLES = 'search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' ');
window.FORGOT_PASSWORD_TITLES = 'forgot geändert vergessen hilfe changeemail español'.split(' ');
window.REMEMBER_ME_TITLES = ['remember me', 'rememberme', 'keep me signed in'];
window.BACK_TITLES = ['back', 'назад'];
// clean up the text
function cleanText(s) {
var sVal = null;
s && (sVal = s.replace(/^\\s+|\\s+$|\\r?\\n.*$/gm, ''), sVal = 0 < sVal.length ? sVal : null);
return sVal;
}
// check the node type and adjust the array accordingly
function checkNodeType(arr, el) {
var theText = '';
3 === el.nodeType ? theText = el.nodeValue : 1 === el.nodeType && (theText = el.textContent || el.innerText);
(theText = cleanText(theText)) && arr.push(theText);
}
function isKnownTag(el) {
if (el && void 0 !== el) {
var tags = 'select option input form textarea button table iframe body head script'.split(' ');
if (el) {
var elTag = el ? (el.tagName || '').toLowerCase() : '';
return tags.constructor == Array ? 0 <= tags.indexOf(elTag) : elTag === tags;
}
else {
return false;
}
}
else {
return true;
}
}
function shiftForLeftLabel(el, arr, steps) {
var sib;
for (steps || (steps = 0) ; el && el.previousSibling;) {
el = el.previousSibling;
if (isKnownTag(el)) {
return;
}
checkNodeType(arr, el);
}
if (el && 0 === arr.length) {
for (sib = null; !sib;) {
el = el.parentElement || el.parentNode;
if (!el) {
return;
}
for (sib = el.previousSibling; sib && !isKnownTag(sib) && sib.lastChild;) {
sib = sib.lastChild;
}
}
// base case and recurse
isKnownTag(sib) || (checkNodeType(arr, sib), 0 === arr.length && shiftForLeftLabel(sib, arr, steps + 1));
}
}
// is a dom element visible on screen?
function isElementVisible(el) {
var theEl = el;
el = (el = el.ownerDocument) ? el.defaultView : {};
// walk the dom tree
for (var elStyle; theEl && theEl !== document;) {
elStyle = el.getComputedStyle ? el.getComputedStyle(theEl, null) : theEl.style;
if (!elStyle) {
return true;
}
if ('none' === elStyle.display || 'hidden' == elStyle.visibility) {
return false;
}
// walk up
theEl = theEl.parentNode;
}
return theEl === document;
}
// is a dom element "viewable" on screen?
function isElementViewable(el) {
var theDoc = el.ownerDocument.documentElement,
rect = el.getBoundingClientRect(),
docScrollWidth = theDoc.scrollWidth,
kosri = theDoc.scrollHeight,
leftOffset = rect.left - theDoc.clientLeft,
topOffset = rect.top - theDoc.clientTop,
theRect;
if (!isElementVisible(el) || !el.offsetParent || 10 > el.clientWidth || 10 > el.clientHeight) {
return false;
}
var rects = el.getClientRects();
if (0 === rects.length) {
return false;
}
for (var i = 0; i < rects.length; i++) {
if (theRect = rects[i], theRect.left > docScrollWidth || 0 > theRect.right) {
return false;
}
}
if (0 > leftOffset || leftOffset > docScrollWidth || 0 > topOffset || topOffset > kosri) {
return false;
}
// walk the tree
for (var pointEl = el.ownerDocument.elementFromPoint(leftOffset + (rect.right > window.innerWidth ? (window.innerWidth - leftOffset) / 2 : rect.width / 2), topOffset + (rect.bottom > window.innerHeight ? (window.innerHeight - topOffset) / 2 : rect.height / 2)) ; pointEl && pointEl !== el && pointEl !== document;) {
if (pointEl.tagName && 'string' === typeof pointEl.tagName && 'label' === pointEl.tagName.toLowerCase()
&& el.labels && 0 < el.labels.length) {
return 0 <= Array.prototype.slice.call(el.labels).indexOf(pointEl);
}
// walk up
pointEl = pointEl.parentNode;
}
return pointEl === el;
}
function getElementForOPID(opId) {
var theEl;
if (void 0 === opId || null === opId) {
return null;
}
try {
var formEls = Array.prototype.slice.call(getFormElements(document));
var filteredFormEls = formEls.filter(function (el) {
return el.opid == opId;
});
if (0 < filteredFormEls.length) {
theEl = filteredFormEls[0], 1 < filteredFormEls.length && console.warn('More than one element found with opid ' + opId);
} else {
var theIndex = parseInt(opId.split('__')[1], 10);
isNaN(theIndex) || (theEl = formEls[theIndex]);
}
} catch (e) {
console.error('An unexpected error occurred: ' + e);
} finally {
return theEl;
}
}
// get all the form elements that we care about
function getFormElements(theDoc) {
var els = [];
try {
els = theDoc.querySelectorAll('input, select, button');
} catch (e) { }
return els;
}
// focus the element and optionally restore its original value
function focusElement(el, setVal) {
if (setVal) {
var initialValue = el.value;
el.focus();
if (el.value !== initialValue) {
el.value = initialValue;
}
} else {
el.focus();
}
}
return JSON.stringify(getPageDetails(document, 'oneshotUUID'));
}
function fill(document, fillScript, undefined) {
var isFirefox = navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1;
var g=!0,k=!0;
function n(a){var b=null;return a?0===a.indexOf('https://')&&'http:'===document.location.protocol&&(b=document.querySelectorAll('input[type=password]'),0<b.length&&(confirmResult=confirm('1Password warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page.\\n\\nDo you still wish to fill this login?'),0==confirmResult))?!0:!1:!1}
function m(a){var b,c=[],d=a.properties,e=1,h,f=[];d&&d.delay_between_operations&&(e=d.delay_between_operations);if(!n(a.savedURL)){h=function(a,b){var d=a[0];if(void 0===d)b();else{if('delay'===d.operation||'delay'===d[0])e=d.parameters?d.parameters[0]:d[1];else{if(d=p(d))for(var l=0;l<d.length;l++)-1===f.indexOf(d[l])&&f.push(d[l]);c=c.concat(f.map(function(a){return a&&a.hasOwnProperty('opid')?a.opid:null}))}setTimeout(function(){h(a.slice(1),b)},e)}};if(b=a.options)b.hasOwnProperty('animate')&&
(k=b.animate),b.hasOwnProperty('markFilling')&&(g=b.markFilling);a.itemType&&'fillPassword'===a.itemType&&(g=!1);a.hasOwnProperty('script')&&(b=a.script,h(b,function(){a.hasOwnProperty('autosubmit')&&'function'==typeof autosubmit&&(a.itemType&&'fillLogin'!==a.itemType||(0<f.length?setTimeout(function(){autosubmit(a.autosubmit,d.allow_clicky_autosubmit,f)},AUTOSUBMIT_DELAY):DEBUG_AUTOSUBMIT&&console.log('[AUTOSUBMIT] Not attempting to submit since no fields were filled: ',f)));'object'==typeof protectedGlobalPage&&
protectedGlobalPage.b('fillItemResults',{documentUUID:documentUUID,fillContextIdentifier:a.fillContextIdentifier,usedOpids:c},function(){fillingItemType=null})}))}}var x={fill_by_opid:q,fill_by_query:r,click_on_opid:s,click_on_query:t,touch_all_fields:u,simple_set_value_by_query:v,focus_by_opid:w,delay:null};
function p(a){var b;if(a.hasOwnProperty('operation')&&a.hasOwnProperty('parameters'))b=a.operation,a=a.parameters;else if('[object Array]'===Object.prototype.toString.call(a))b=a[0],a=a.splice(1);else return null;return x.hasOwnProperty(b)?x[b].apply(this,a):null}function q(a,b){var c;return(c=y(a))?(z(c,b),[c]):null}function r(a,b){var c;c=A(a);return Array.prototype.map.call(Array.prototype.slice.call(c),function(a){z(a,b);return a},this)}
function v(a,b){var c,d=[];c=A(a);Array.prototype.forEach.call(Array.prototype.slice.call(c),function(a){a.disabled||a.a||a.readOnly||void 0===a.value||(a.value=b,d.push(a))});return d}function w(a){if(a=y(a))'function'===typeof a.click&&a.click(),'function'===typeof a.focus&&B(a,!0);return null}function s(a){return(a=y(a))?C(a)?[a]:null:null}
function t(a){a=A(a);return Array.prototype.map.call(Array.prototype.slice.call(a),function(a){C(a);'function'===typeof a.click&&a.click();'function'===typeof a.focus&&B(a,!0);return[a]},this)}function u(){D()};var E={'true':!0,y:!0,1:!0,yes:!0,'✓':!0},F=200;function z(a,b){var c;if(a&&null!==b&&void 0!==b&&!(a.disabled||a.a||a.readOnly))switch(g&&a.form&&!a.form.opfilled&&(a.form.opfilled=!0),a.type?a.type.toLowerCase():null){case 'checkbox':c=b&&1<=b.length&&E.hasOwnProperty(b.toLowerCase())&&!0===E[b.toLowerCase()];a.checked===c||G(a,function(a){a.checked=c});break;case 'radio':!0===E[b.toLowerCase()]&&a.click();break;default:a.value==b||G(a,function(a){a.value=b})}}
function G(a,b){H(a);b(a);I(a);J(a)&&(a.className+=' com-agilebits-onepassword-extension-animated-fill',setTimeout(function(){a&&a.className&&(a.className=a.className.replace(/(\\s)?com-agilebits-onepassword-extension-animated-fill/,''))},F))};document.elementForOPID=y;function K(a,b){var c;isFirefox?(c=document.createEvent('KeyboardEvent'),c.initKeyEvent(b,!0,!1,null,!1,!1,!1,!1,0,0)):(c=a.ownerDocument.createEvent('Events'),c.initEvent(b,!0,!1),c.charCode=0,c.keyCode=0,c.which=0,c.srcElement=a,c.target=a);return c}function H(a){var b=a.value;C(a);B(a,!1);a.dispatchEvent(K(a,'keydown'));a.dispatchEvent(K(a,'keypress'));a.dispatchEvent(K(a,'keyup'));a.value!==b&&(a.value=b)}
function I(a){var b=a.value,c=a.ownerDocument.createEvent('HTMLEvents'),d=a.ownerDocument.createEvent('HTMLEvents');a.dispatchEvent(K(a,'keydown'));a.dispatchEvent(K(a,'keypress'));a.dispatchEvent(K(a,'keyup'));d.initEvent('input',!0,!0);a.dispatchEvent(d);c.initEvent('change',!0,!0);a.dispatchEvent(c);a.blur();a.value!==b&&(a.value=b)}function C(a){if(!a||a&&'function'!==typeof a.click)return!1;a.click();return!0}
function L(){var a=RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|passe|contraseña|senha|密码|adgangskode|hasło|wachtwoord)','i');return Array.prototype.slice.call(A("input[type='text']")).filter(function(b){return b.value&&a.test(b.value)},this)}function D(){L().forEach(function(a){H(a);a.click&&a.click();I(a)})}
window.LOGIN_TITLES=[/^\\W*log\\W*[oi]n\\W*$/i,/log\\W*[oi]n (?:securely|now)/i,/^\\W*sign\\W*[oi]n\\W*$/i,'continue','submit','weiter','accès','вход','connexion','entrar','anmelden','accedi','valider','登录','लॉग इन करें','change password'];window.LOGIN_RED_HERRING_TITLES=['already have an account','sign in with'];window.REGISTER_TITLES='register;sign up;signup;join;create my account;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';');
window.SEARCH_TITLES='search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' ');window.FORGOT_PASSWORD_TITLES='forgot geändert vergessen hilfe changeemail español'.split(' ');window.REMEMBER_ME_TITLES=['remember me','rememberme','keep me signed in'];window.BACK_TITLES=['back','назад'];
function J(a){var b;if(b=k)a:{b=a;for(var c=a.ownerDocument,c=c?c.defaultView:{},d;b&&b!==document;){d=c.getComputedStyle?c.getComputedStyle(b,null):b.style;if(!d){b=!0;break a}if('none'===d.display||'hidden'==d.visibility){b=!1;break a}b=b.parentNode}b=b===document}return b?-1!=='email text password number tel url'.split(' ').indexOf(a.type||''):!1}
function y(a){var b;if(void 0===a||null===a)return null;try{var c=Array.prototype.slice.call(A('input, select, button')),d=c.filter(function(b){return b.opid==a});if(0<d.length)b=d[0],1<d.length&&console.warn('More than one element found with opid '+a);else{var e=parseInt(a.split('__')[1],10);isNaN(e)||(b=c[e])}}catch(h){console.error('An unexpected error occurred: '+h)}finally{return b}};function A(a){var b=document,c=[];try{c=b.querySelectorAll(a)}catch(d){}return c}function B(a,b){if(b){var c=a.value;a.focus();a.value!==c&&(a.value=c)}else a.focus()};
m(fillScript);
return JSON.stringify({'success': true});
var markTheFilling = true,
animateTheFilling = true;
// Check if URL is not secure when the original saved one was
function urlNotSecure(savedURL) {
var passwordInputs = null;
if (!savedURL) {
return false;
}
return 0 === savedURL.indexOf('https://') && 'http:' === document.location.protocol && (passwordInputs = document.querySelectorAll('input[type=password]'),
0 < passwordInputs.length && (confirmResult = confirm('Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page.\\n\\nDo you still wish to fill this login?'),
0 == confirmResult)) ? true : false;
}
function doFill(fillScript) {
var fillScriptOps,
theOpIds = [],
fillScriptProperties = fillScript.properties,
operationDelayMs = 1,
doOperation,
operationsToDo = [];
fillScriptProperties &&
fillScriptProperties.delay_between_operations &&
(operationDelayMs = fillScriptProperties.delay_between_operations);
if (urlNotSecure(fillScript.savedURL)) {
return;
}
doOperation = function (ops, theOperation) {
var op = ops[0];
if (void 0 === op) {
theOperation();
} else {
// should we delay?
if ('delay' === op.operation || 'delay' === op[0]) {
operationDelayMs = op.parameters ? op.parameters[0] : op[1];
} else {
if (op = normalizeOp(op)) {
for (var opIndex = 0; opIndex < op.length; opIndex++) {
-1 === operationsToDo.indexOf(op[opIndex]) && operationsToDo.push(op[opIndex]);
}
}
theOpIds = theOpIds.concat(operationsToDo.map(function (operationToDo) {
return operationToDo && operationToDo.hasOwnProperty('opid') ? operationToDo.opid : null;
}));
}
setTimeout(function () {
doOperation(ops.slice(1), theOperation);
}, operationDelayMs);
}
};
if (fillScriptOps = fillScript.options) {
fillScriptOps.hasOwnProperty('animate') && (animateTheFilling = fillScriptOps.animate),
fillScriptOps.hasOwnProperty('markFilling') && (markTheFilling = fillScriptOps.markFilling);
}
// don't mark a password filling
fillScript.itemType && 'fillPassword' === fillScript.itemType && (markTheFilling = false);
if (!fillScript.hasOwnProperty('script')) {
return;
}
// custom fill script
fillScriptOps = fillScript.script;
doOperation(fillScriptOps, function () {
// Done now
// Do we have anything to autosubmit?
if (fillScript.hasOwnProperty('autosubmit') && 'function' == typeof autosubmit) {
fillScript.itemType && 'fillLogin' !== fillScript.itemType || (0 < operationsToDo.length ? setTimeout(function () {
autosubmit(fillScript.autosubmit, fillScriptProperties.allow_clicky_autosubmit, operationsToDo);
}, AUTOSUBMIT_DELAY) : DEBUG_AUTOSUBMIT && console.log('[AUTOSUBMIT] Not attempting to submit since no fields were filled: ', operationsToDo))
}
// handle protectedGlobalPage
if ('object' == typeof protectedGlobalPage) {
protectedGlobalPage.b('fillItemResults', {
documentUUID: documentUUID,
fillContextIdentifier: fillScript.fillContextIdentifier,
usedOpids: theOpIds
}, function () {
fillingItemType = null;
})
}
});
}
// fill for reference
var thisFill = {
fill_by_opid: doFillByOpId,
fill_by_query: doFillByQuery,
click_on_opid: doClickByOpId,
click_on_query: doClickByQuery,
touch_all_fields: touchAllFields,
simple_set_value_by_query: doSimpleSetByQuery,
focus_by_opid: doFocusByOpId,
delay: null
};
// normalize the op versus the reference
function normalizeOp(op) {
var thisOperation;
if (op.hasOwnProperty('operation') && op.hasOwnProperty('parameters')) {
thisOperation = op.operation, op = op.parameters;
} else {
if ('[object Array]' === Object.prototype.toString.call(op)) {
thisOperation = op[0],
op = op.splice(1);
} else {
return null;
}
}
return thisFill.hasOwnProperty(thisOperation) ? thisFill[thisOperation].apply(this, op) : null;
}
// do a fill by opid operation
function doFillByOpId(opId, op) {
var el = getElementByOpId(opId);
return el ? (fillTheElement(el, op), [el]) : null;
}
// do a fill by query operation
function doFillByQuery(query, op) {
var elements = selectAllFromDoc(query);
return Array.prototype.map.call(Array.prototype.slice.call(elements), function (el) {
fillTheElement(el, op);
return el;
}, this);
}
// do a simple set value by query
function doSimpleSetByQuery(query, valueToSet) {
var elements = selectAllFromDoc(query),
arr = [];
Array.prototype.forEach.call(Array.prototype.slice.call(elements), function (el) {
el.disabled || el.a || el.readOnly || void 0 === el.value || (el.value = valueToSet, arr.push(el));
});
return arr;
}
// focus by opid
function doFocusByOpId(opId) {
var el = getElementByOpId(opId)
if (el) {
'function' === typeof el.click && el.click(),
'function' === typeof el.focus && doFocusElement(el, true);
}
return null;
}
// do a click by opid operation
function doClickByOpId(opId) {
var el = getElementByOpId(opId);
return el ? clickElement(el) ? [el] : null : null;
}
// do a click by query operation
function doClickByQuery(query) {
query = selectAllFromDoc(query);
return Array.prototype.map.call(Array.prototype.slice.call(query), function (el) {
clickElement(el);
'function' === typeof el.click && el.click();
'function' === typeof el.focus && doFocusElement(el, true);
return [el];
}, this);
}
var checkRadioTrueOps = {
'true': true,
y: true,
1: true,
yes: true,
'✓': true
},
styleTimeout = 200;
// fill an element
function fillTheElement(el, op) {
var shouldCheck;
if (el && null !== op && void 0 !== op && !(el.disabled || el.a || el.readOnly)) {
switch (markTheFilling && el.form && !el.form.opfilled && (el.form.opfilled = true),
el.type ? el.type.toLowerCase() : null) {
case 'checkbox':
shouldCheck = op && 1 <= op.length && checkRadioTrueOps.hasOwnProperty(op.toLowerCase()) && true === checkRadioTrueOps[op.toLowerCase()];
el.checked === shouldCheck || doAllFillOperations(el, function (theEl) {
theEl.checked = shouldCheck;
});
break;
case 'radio':
true === checkRadioTrueOps[op.toLowerCase()] && el.click();
break;
default:
el.value == op || doAllFillOperations(el, function (theEl) {
theEl.value = op;
});
}
}
}
// do all the full operations needed
function doAllFillOperations(el, afterValSetFunc) {
setValueForElement(el);
afterValSetFunc(el);
setValueForElementByEvent(el);
canSeeElementToStyle(el) && (el.className += ' com-agilebits-onepassword-extension-animated-fill',
setTimeout(function () {
el && el.className && (el.className = el.className.replace(/(\\s)?com-agilebits-onepassword-extension-animated-fill/, ''));
}, styleTimeout));
}
document.elementForOPID = getElementByOpId;
// normalize the event since firefox handles events differently than others
function normalizeEvent(el, eventName) {
var ev;
if (isFirefox) {
ev = document.createEvent('KeyboardEvent');
ev.initKeyEvent(eventName, true, false, null, false, false, false, false, 0, 0);
}
else {
ev = el.ownerDocument.createEvent('Events');
ev.initEvent(eventName, true, false);
ev.charCode = 0;
ev.keyCode = 0;
ev.which = 0;
ev.srcElement = el;
ev.target = el;
}
return ev;
}
// set value of the given element
function setValueForElement(el) {
var valueToSet = el.value;
clickElement(el);
doFocusElement(el, false);
el.dispatchEvent(normalizeEvent(el, 'keydown'));
el.dispatchEvent(normalizeEvent(el, 'keypress'));
el.dispatchEvent(normalizeEvent(el, 'keyup'));
el.value !== valueToSet && (el.value = valueToSet);
}
// set value of the given element by using events
function setValueForElementByEvent(el) {
var valueToSet = el.value,
ev1 = el.ownerDocument.createEvent('HTMLEvents'),
ev2 = el.ownerDocument.createEvent('HTMLEvents');
el.dispatchEvent(normalizeEvent(el, 'keydown'));
el.dispatchEvent(normalizeEvent(el, 'keypress'));
el.dispatchEvent(normalizeEvent(el, 'keyup'));
ev2.initEvent('input', true, true);
el.dispatchEvent(ev2);
ev1.initEvent('change', true, true);
el.dispatchEvent(ev1);
el.blur();
el.value !== valueToSet && (el.value = valueToSet);
}
// click on an element
function clickElement(el) {
if (!el || el && 'function' !== typeof el.click) {
return false;
}
el.click();
return true;
}
// get all fields we care about
function getAllFields() {
var r = RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|passe|contraseña|senha|密码|adgangskode|hasło|wachtwoord)', 'i');
return Array.prototype.slice.call(selectAllFromDoc("input[type='text']")).filter(function (el) {
return el.value && r.test(el.value);
}, this);
}
// touch all the fields
function touchAllFields() {
getAllFields().forEach(function (el) {
setValueForElement(el);
el.click && el.click();
setValueForElementByEvent(el);
});
}
// some useful globals
window.LOGIN_TITLES = [/^\\W*log\\W*[oi]n\\W*$/i, /log\\W*[oi]n (?:securely|now)/i, /^\\W*sign\\W*[oi]n\\W*$/i, 'continue', 'submit', 'weiter', 'accès', 'вход', 'connexion', 'entrar', 'anmelden', 'accedi', 'valider', '登录', 'लॉग इन करें', 'change password'];
window.LOGIN_RED_HERRING_TITLES = ['already have an account', 'sign in with'];
window.REGISTER_TITLES = 'register;sign up;signup;join;create my account;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';');
window.SEARCH_TITLES = 'search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' ');
window.FORGOT_PASSWORD_TITLES = 'forgot geändert vergessen hilfe changeemail español'.split(' ');
window.REMEMBER_ME_TITLES = ['remember me', 'rememberme', 'keep me signed in'];
window.BACK_TITLES = ['back', 'назад'];
// can we see the element to apply some styling?
function canSeeElementToStyle(el) {
var currentEl;
if (currentEl = animateTheFilling) {
a: {
currentEl = el;
for (var owner = el.ownerDocument, owner = owner ? owner.defaultView : {}, theStyle; currentEl && currentEl !== document;) {
theStyle = owner.getComputedStyle ? owner.getComputedStyle(currentEl, null) : currentEl.style;
if (!theStyle) {
currentEl = true;
break a;
}
if ('none' === theStyle.display || 'hidden' == theStyle.visibility) {
currentEl = false;
break a;
}
currentEl = currentEl.parentNode;
}
currentEl = currentEl === document;
}
}
return currentEl ? -1 !== 'email text password number tel url'.split(' ').indexOf(el.type || '') : false;
}
// find the element for this operation
function getElementByOpId(theOpId) {
var theElement;
if (void 0 === theOpId || null === theOpId) {
return null;
}
try {
var elements = Array.prototype.slice.call(selectAllFromDoc('input, select, button'));
var filteredElements = elements.filter(function (o) {
return o.opid == theOpId;
});
if (0 < filteredElements.length) {
theElement = filteredElements[0],
1 < filteredElements.length && console.warn('More than one element found with opid ' + theOpId);
} else {
var elIndex = parseInt(theOpId.split('__')[1], 10);
isNaN(elIndex) || (theElement = elements[elIndex]);
}
} catch (e) {
console.error('An unexpected error occurred: ' + e);
} finally {
return theElement;
}
}
// helper for doc.querySelectorAll
function selectAllFromDoc(theSelector) {
var d = document, elements = [];
try {
elements = d.querySelectorAll(theSelector);
} catch (e) { }
return elements;
}
// focus an element and optionally re-set its value after focusing
function doFocusElement(el, setValue) {
if (setValue) {
var existingValue = el.value;
el.focus();
el.value !== existingValue && (el.value = existingValue);
} else {
el.focus();
}
}
doFill(fillScript);
return JSON.stringify({
success: true
});
}
/*

View File

@ -81,7 +81,7 @@
}
icons = [];
var pageDetails = JSON.parse(collect(document));
var pageDetails = null; TODO: collect //JSON.parse(collect(document));
if (pageDetails) {
var iconFields = [];
@ -207,83 +207,4 @@
return element;
}
}
/*
1Password Extension
Lovingly handcrafted by Dave Teare, Michael Fey, Rad Azzouz, and Roustem Karimov.
Copyright (c) 2014 AgileBits. All rights reserved.
================================================================================
Copyright (c) 2014 AgileBits Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
function collect(document, undefined) {
document.elementsByOPID = {}; document.addEventListener('input', function (c) { !1 !== c.a && 'input' === c.target.tagName.toLowerCase() && (c.target.dataset['com.agilebits.onepassword.userEdited'] = 'yes') }, !0);
function r(c, e) {
function b(a, g) { var d = a[g]; if ('string' == typeof d) return d; d = a.getAttribute(g); return 'string' == typeof d ? d : null } function h(a, g) { if (-1 === ['text', 'password'].indexOf(g.type.toLowerCase()) || !(n.test(a.value) || n.test(a.htmlID) || n.test(a.htmlName) || n.test(a.placeholder) || n.test(a['label-tag']) || n.test(a['label-data']) || n.test(a['label-aria']))) return !1; if (!a.visible) return !0; if ('password' == g.type.toLowerCase()) return !1; var d = g.type; w(g, !0); return d !== g.type } function p(a) {
switch (q(a.type)) {
case 'checkbox': return a.checked ?
'✓' : ''; case 'hidden': a = a.value; if (!a || 'number' != typeof a.length) return ''; 254 < a.length && (a = a.substr(0, 254) + '...SNIPPED'); return a; default: return a.value
}
} function m(a) { return a.options ? (a = Array.prototype.slice.call(a.options).map(function (a) { var d = a.text, d = d ? q(d).replace(/\\s/mg, '').replace(/[~`!@$%^&*()\\-_+=:;'\"\\[\\]|\\\\,<.>\\?]/mg, '') : null; return [d ? d : null, a.value] }), { options: a }) : null } function s(a) {
var c; for (a = a.parentElement || a.parentNode; a && 'td' != q(a.tagName) ;) a = a.parentElement || a.parentNode; if (!a ||
void 0 === a) return null; c = a.parentElement || a.parentNode; if ('tr' != c.tagName.toLowerCase()) return null; c = c.previousElementSibling; if (!c || 'tr' != (c.tagName + '').toLowerCase() || c.cells && a.cellIndex >= c.cells.length) return null; a = c.cells[a.cellIndex]; a = a.textContent || a.innerText; return a = y(a)
} function t(a) {
var g, d = []; if (a.labels && a.labels.length && 0 < a.labels.length) d = Array.prototype.slice.call(a.labels); else {
a.id && (d = d.concat(Array.prototype.slice.call(x(c, 'label[for=' + JSON.stringify(a.id) + ']')))); if (a.name) {
g =
x(c, 'label[for=' + JSON.stringify(a.name) + ']'); for (var b = 0; b < g.length; b++) -1 === d.indexOf(g[b]) && d.push(g[b])
} for (g = a; g && g != c; g = g.parentNode) 'label' === q(g.tagName) && -1 === d.indexOf(g) && d.push(g)
} 0 === d.length && (g = a.parentNode, 'dd' === g.tagName.toLowerCase() && 'dt' === g.previousElementSibling.tagName.toLowerCase() && d.push(g.previousElementSibling)); return 0 < d.length ? d.map(function (a) { return (a.textContent || a.innerText).replace(/^\\s+/, '').replace(/\\s+$/, '').replace('\\n', '').replace(/\\s{2,}/, ' ') }).join('') :
null
} function f(a, c, d, b) { void 0 !== b && b === d || null === d || void 0 === d || (a[c] = d) } function q(a) { return 'string' === typeof a ? a.toLowerCase() : ('' + a).toLowerCase() } function x(a, c) { var d = []; try { d = a.querySelectorAll(c) } catch (b) { } return d } var u = c.defaultView ? c.defaultView : window, n = RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|(\\\\b|_|-)passe(\\\\b|_|-)|contraseña|senha|密码|adgangskode|hasło|wachtwoord)', 'i'), v = Array.prototype.slice.call(x(c, 'form')).map(function (a, c) {
var d = {}, e = '__form__' + c; a.opid = e; d.opid =
e; f(d, 'htmlName', b(a, 'name')); f(d, 'htmlID', b(a, 'id')); e = b(a, 'action'); e = new URL(e, window.location.href); f(d, 'htmlAction', e ? e.href : null); f(d, 'htmlMethod', b(a, 'method')); return d
}), F = Array.prototype.slice.call(z(c)).map(function (a, e) {
var d = {}, l = '__' + e, k = -1 == a.maxLength ? 999 : a.maxLength; if (!k || 'number' === typeof k && isNaN(k)) k = 999; c.elementsByOPID[l] = a; a.opid = l; d.opid = l; d.elementNumber = e; f(d, 'maxLength', Math.min(k, 999), 999); d.visible = A(a); d.viewable = B(a); f(d, 'htmlID', b(a, 'id')); f(d, 'htmlName', b(a, 'name'));
f(d, 'htmlClass', b(a, 'class')); f(d, 'tabindex', b(a, 'tabindex')); f(d, 'title', b(a, 'title')); f(d, 'userEdited', !!a.dataset['com.agilebits.onepassword.userEdited']); if ('hidden' != q(a.type)) { f(d, 'label-tag', t(a)); f(d, 'label-data', b(a, 'data-label')); f(d, 'label-aria', b(a, 'aria-label')); f(d, 'label-top', s(a)); l = []; for (k = a; k && k.nextSibling;) { k = k.nextSibling; if (C(k)) break; D(l, k) } f(d, 'label-right', l.join('')); l = []; E(a, l); l = l.reverse().join(''); f(d, 'label-left', l); f(d, 'placeholder', b(a, 'placeholder')) } f(d, 'rel', b(a,
'rel')); f(d, 'type', q(b(a, 'type'))); f(d, 'value', p(a)); f(d, 'checked', a.checked, !1); f(d, 'autoCompleteType', a.getAttribute('x-autocompletetype') || a.getAttribute('autocompletetype') || a.getAttribute('autocomplete'), 'off'); f(d, 'disabled', a.disabled); f(d, 'readonly', a.b || a.readOnly); f(d, 'selectInfo', m(a)); f(d, 'aria-hidden', 'true' == a.getAttribute('aria-hidden'), !1); f(d, 'aria-disabled', 'true' == a.getAttribute('aria-disabled'), !1); f(d, 'aria-haspopup', 'true' == a.getAttribute('aria-haspopup'), !1); f(d, 'data-unmasked',
a.dataset.unmasked); f(d, 'data-stripe', b(a, 'data-stripe')); f(d, 'onepasswordFieldType', a.dataset.onepasswordFieldType || a.type); f(d, 'onepasswordDesignation', a.dataset.onepasswordDesignation); f(d, 'onepasswordSignInUrl', a.dataset.onepasswordSignInUrl); f(d, 'onepasswordSectionTitle', a.dataset.onepasswordSectionTitle); f(d, 'onepasswordSectionFieldKind', a.dataset.onepasswordSectionFieldKind); f(d, 'onepasswordSectionFieldTitle', a.dataset.onepasswordSectionFieldTitle); f(d, 'onepasswordSectionFieldValue', a.dataset.onepasswordSectionFieldValue);
a.form && (d.form = b(a.form, 'opid')); f(d, 'fakeTested', h(d, a), !1); return d
}); F.filter(function (a) { return a.fakeTested }).forEach(function (a) {
var b = c.elementsByOPID[a.opid]; b.getBoundingClientRect(); var d = b.value; !b || b && 'function' !== typeof b.click || b.click(); w(b, !1); b.dispatchEvent(G(b, 'keydown')); b.dispatchEvent(G(b, 'keypress')); b.dispatchEvent(G(b, 'keyup')); b.value !== d && (b.value = d); b.click && b.click(); a.postFakeTestVisible = A(b); a.postFakeTestViewable = B(b); a.postFakeTestType = b.type; a = b.value; var d = b.ownerDocument.createEvent('HTMLEvents'),
e = b.ownerDocument.createEvent('HTMLEvents'); b.dispatchEvent(G(b, 'keydown')); b.dispatchEvent(G(b, 'keypress')); b.dispatchEvent(G(b, 'keyup')); e.initEvent('input', !0, !0); b.dispatchEvent(e); d.initEvent('change', !0, !0); b.dispatchEvent(d); b.blur(); b.value !== a && (b.value = a)
}); u = { documentUUID: e, title: c.title, url: u.location.href, documentUrl: c.location.href, tabUrl: u.location.href, forms: function (a) { var b = {}; a.forEach(function (a) { b[a.opid] = a }); return b }(v), fields: F, collectedTimestamp: (new Date).getTime() }; (v = document.querySelector('[data-onepassword-title]')) &&
v.dataset[DISPLAY_TITLE_ATTRIBUE] && (u.displayTitle = v.dataset.onepasswordTitle); return u
}; document.elementForOPID = H; function G(c, e) { var b; I ? (b = document.createEvent('KeyboardEvent'), b.initKeyEvent(e, !0, !1, null, !1, !1, !1, !1, 0, 0)) : (b = c.ownerDocument.createEvent('Events'), b.initEvent(e, !0, !1), b.charCode = 0, b.keyCode = 0, b.which = 0, b.srcElement = c, b.target = c); return b } window.LOGIN_TITLES = [/^\\W*log\\W*[oi]n\\W*$/i, /log\\W*[oi]n (?:securely|now)/i, /^\\W*sign\\W*[oi]n\\W*$/i, 'continue', 'submit', 'weiter', 'accès', 'вход', 'connexion', 'entrar', 'anmelden', 'accedi', 'valider', '登录', 'लॉग इन करें', 'change password'];
window.LOGIN_RED_HERRING_TITLES = ['already have an account', 'sign in with']; window.REGISTER_TITLES = 'register;sign up;signup;join;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';'); window.SEARCH_TITLES = 'search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' '); window.FORGOT_PASSWORD_TITLES = 'forgot geändert vergessen hilfe changeemail español'.split(' '); window.REMEMBER_ME_TITLES = ['remember me', 'rememberme', 'keep me signed in'];
window.BACK_TITLES = ['back', 'назад']; function y(c) { var e = null; c && (e = c.replace(/^\\s+|\\s+$|\\r?\\n.*$/mg, ''), e = 0 < e.length ? e : null); return e } function D(c, e) { var b; b = ''; 3 === e.nodeType ? b = e.nodeValue : 1 === e.nodeType && (b = e.textContent || e.innerText); (b = y(b)) && c.push(b) } function C(c) { var e; c && void 0 !== c ? (e = 'select option input form textarea button table iframe body head script'.split(' '), c ? (c = c ? (c.tagName || '').toLowerCase() : '', e = e.constructor == Array ? 0 <= e.indexOf(c) : c === e) : e = !1) : e = !0; return e }
function E(c, e, b) { var h; for (b || (b = 0) ; c && c.previousSibling;) { c = c.previousSibling; if (C(c)) return; D(e, c) } if (c && 0 === e.length) { for (h = null; !h;) { c = c.parentElement || c.parentNode; if (!c) return; for (h = c.previousSibling; h && !C(h) && h.lastChild;) h = h.lastChild } C(h) || (D(e, h), 0 === e.length && E(h, e, b + 1)) } }
function A(c) { var e = c; c = (c = c.ownerDocument) ? c.defaultView : {}; for (var b; e && e !== document;) { b = c.getComputedStyle ? c.getComputedStyle(e, null) : e.style; if (!b) return !0; if ('none' === b.display || 'hidden' == b.visibility) return !1; e = e.parentNode } return e === document }
function B(c) {
var e = c.ownerDocument.documentElement, b = c.getBoundingClientRect(), h = e.scrollWidth, p = e.scrollHeight, m = b.left - e.clientLeft, e = b.top - e.clientTop, s; if (!A(c) || !c.offsetParent || 10 > c.clientWidth || 10 > c.clientHeight) return !1; var t = c.getClientRects(); if (0 === t.length) return !1; for (var f = 0; f < t.length; f++) if (s = t[f], s.left > h || 0 > s.right) return !1; if (0 > m || m > h || 0 > e || e > p) return !1; for (b = c.ownerDocument.elementFromPoint(m + (b.right > window.innerWidth ? (window.innerWidth - m) / 2 : b.width / 2), e + (b.bottom > window.innerHeight ?
(window.innerHeight - e) / 2 : b.height / 2)) ; b && b !== c && b !== document;) { if (b.tagName && 'string' === typeof b.tagName && 'label' === b.tagName.toLowerCase() && c.labels && 0 < c.labels.length) return 0 <= Array.prototype.slice.call(c.labels).indexOf(b); b = b.parentNode } return b === c
}
function H(c) { var e; if (void 0 === c || null === c) return null; try { var b = Array.prototype.slice.call(z(document)), h = b.filter(function (b) { return b.opid == c }); if (0 < h.length) e = h[0], 1 < h.length && console.warn('More than one element found with opid ' + c); else { var p = parseInt(c.split('__')[1], 10); isNaN(p) || (e = b[p]) } } catch (m) { console.error('An unexpected error occurred: ' + m) } finally { return e } }; var I = 'object' === typeof tabs || 'object' === typeof self && 'object' === typeof self.port; function z(c) { var e = []; try { e = c.querySelectorAll('input, select, button') } catch (b) { } return e } function w(c, e) { if (e) { var b = c.value; c.focus(); c.value !== b && (c.value = b) } else c.focus() };
return JSON.stringify(r(document, 'oneshotUUID'));
}
/*
End 1Password Extension
*/
})();

View File

@ -3,18 +3,19 @@
formData = [],
barType = null;
chrome.storage.local.get('disableAddLoginNotification', function (obj) {
if (!obj || !obj['disableAddLoginNotification']) {
chrome.runtime.sendMessage({
command: 'bgCollectPageDetails'
});
}
});
if (window.location.hostname.indexOf('bitwarden.com') === -1) {
chrome.storage.local.get('disableAddLoginNotification', function (obj) {
if (!obj || !obj['disableAddLoginNotification']) {
chrome.runtime.sendMessage({
command: 'bgCollectPageDetails'
});
}
});
}
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.command === 'openNotificationBar') {
closeBar(false);
openBar(msg.data.type, msg.data.typeData);
closeExistingAndOpenBar(msg.data.type, msg.data.typeData);
sendResponse();
return true;
}
@ -50,39 +51,63 @@
}
if (form) {
forms[i].formElement = form;
formData.push(forms[i]);
var formDataObj = {
data: forms[i],
formEl: form,
usernameEl: null,
passwordEl: null
};
locateFields(formDataObj);
formData.push(formDataObj);
form.addEventListener('submit', formSubmitted, false);
}
}
}
function locateFields(formDataObj) {
var passwordId = formDataObj.data.password ? formDataObj.data.password.htmlID : null,
usernameId = formDataObj.data.username ? formDataObj.data.username.htmlID : null,
passwordName = formDataObj.data.password ? formDataObj.data.password.htmlName : null,
usernameName = formDataObj.data.username ? formDataObj.data.username.htmlName : null,
inputs = document.getElementsByTagName('input');
if (passwordId && passwordId !== '') {
formDataObj.passwordEl = formDataObj.formEl.querySelector('#' + passwordId);
}
if (!formDataObj.passwordEl && passwordName !== '') {
formDataObj.passwordEl = formDataObj.formEl.querySelector('input[name="' + passwordName + '"]');
}
if (!formDataObj.passwordEl && formDataObj.passwordEl) {
formDataObj.passwordEl = inputs[formDataObj.data.password.elementNumber];
if (formDataObj.passwordEl && formDataObj.passwordEl.type !== 'password') {
formDataObj.passwordEl = null;
}
}
if (!formDataObj.passwordEl) {
formDataObj.passwordEl = formDataObj.formEl.querySelector('input[type="password"]');
}
if (usernameId && usernameId !== '') {
formDataObj.usernameEl = formDataObj.formEl.querySelector('#' + usernameId);
}
if (!formDataObj.usernameEl && usernameName !== '') {
formDataObj.usernameEl = formDataObj.formEl.querySelector('input[name="' + usernameName + '"]');
}
if (!formDataObj.usernameEl && formDataObj.data.username) {
formDataObj.usernameEl = inputs[formDataObj.data.username.elementNumber];
}
}
function formSubmitted(e) {
for (var i = 0; i < formData.length; i++) {
if (formData[i].formElement === e.target) {
var password = null,
username = null,
passwordId = formData[i].password ? formData[i].password.htmlID : null,
usernameId = formData[i].username ? formData[i].username.htmlID : null,
inputs = document.getElementsByTagName('input');
if (passwordId && passwordId !== '') {
password = document.getElementById(passwordId);
}
else if (formData[i].password) {
password = inputs[formData[i].password.elementNumber];
}
if (usernameId && usernameId !== '') {
username = document.getElementById(usernameId);
}
else if (formData[i].username) {
username = inputs[formData[i].username.elementNumber];
if (formData[i].formEl === e.target) {
if (!formData[i].usernameEl || !formData[i].passwordEl) {
break;
}
var login = {
username: username.value,
password: password.value,
username: formData[i].usernameEl.value,
password: formData[i].passwordEl.value,
url: document.URL
};
@ -97,9 +122,8 @@
}
}
function openBar(type, typeData) {
function closeExistingAndOpenBar(type, typeData) {
var barPage = 'notification/bar.html';
barType = type;
switch (type) {
case 'info':
barPage = barPage + '?info=' + typeData.text;
@ -120,6 +144,18 @@
break;
}
var frame = document.getElementById('bit-notification-bar-iframe');
if (frame && frame.src.indexOf(barPage) >= 0) {
return;
}
closeBar(false);
openBar(type, barPage);
}
function openBar(type, barPage) {
barType = type;
if (!document.body) {
return;
}
@ -127,6 +163,7 @@
var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL(barPage);
iframe.style.cssText = 'height: 42px; width: 100%; border: 0;';
iframe.id = 'bit-notification-bar-iframe';
var frameDiv = document.createElement('div');
frameDiv.id = 'bit-notification-bar';

View File

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "1.7.0",
"version": "1.9.2",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "8bit Solutions LLC",
@ -31,11 +31,9 @@
"background": {
"scripts": [
"lib/jquery/jquery.js",
"lib/sjcl/sjcl.js",
"lib/sjcl/cbc.js",
"lib/sjcl/bitArray.js",
"lib/q/q.js",
"lib/tldjs/tld.js",
"lib/forge/forge.js",
"models/api/requestModels.js",
"models/api/responseModels.js",
"models/dataModels.js",
@ -47,6 +45,7 @@
"services/tokenService.js",
"services/apiService.js",
"services/userService.js",
"services/settingsService.js",
"services/folderService.js",
"services/loginService.js",
"services/syncService.js",
@ -71,6 +70,7 @@
"contextMenus",
"storage",
"unlimitedStorage",
"clipboardWrite",
"http://*/*",
"https://*/*"
],

View File

@ -12,13 +12,39 @@ var FolderRequest = function (folder) {
this.name = folder.name ? folder.name.encryptedString : null;
};
var TokenRequest = function (email, masterPasswordHash, device) {
var TokenRequest = function (email, masterPasswordHash, token, device) {
this.email = email;
this.masterPasswordHash = masterPasswordHash;
if (device) {
this.device = new DeviceRequest(device);
}
this.token = token;
this.provider = 0; // 0 = Authenticator
this.device = null;
if (device) {
this.device = device;
}
this.toIdentityToken = function () {
var obj = {
grant_type: 'password',
username: this.email,
password: this.masterPasswordHash,
scope: 'api offline_access',
client_id: 'browser'
};
if (this.device) {
obj.deviceType = this.device.type;
obj.deviceIdentifier = this.device.identifier;
obj.deviceName = this.device.name;
obj.devicePushToken = this.device.pushToken;
}
if (this.token && this.provider != null && (typeof this.provider !== 'undefined')) {
obj.twoFactorToken = this.token;
obj.twoFactorProvider = this.provider;
}
return obj;
};
};
var RegisterRequest = function (email, masterPasswordHash, masterPasswordHint) {
@ -32,19 +58,13 @@ var PasswordHintRequest = function (email) {
this.email = email;
};
var TokenTwoFactorRequest = function (code) {
this.code = code;
this.provider = "Authenticator";
this.device = null;
};
var DeviceTokenRequest = function () {
this.pushToken = null;
};
var DeviceRequest = function () {
this.type = null;
this.name = null;
this.identifier = null;
var DeviceRequest = function (appId, utilsService) {
this.type = utilsService.getDeviceType();
this.name = utilsService.getBrowser();
this.identifier = appId;
this.pushToken = null;
};

View File

@ -38,22 +38,31 @@ var ProfileResponse = function (response) {
this.twoFactorEnabled = response.TwoFactorEnabled;
};
var TokenResponse = function (response) {
this.token = response.Token;
var IdentityTokenResponse = function (response) {
this.accessToken = response.access_token;
this.expiresIn = response.expires_in;
this.refreshToken = response.refresh_token;
this.tokenType = response.token_type;
if (response.Profile) {
this.profile = new ProfileResponse(response.Profile);
}
// TODO: extras
};
var ListResponse = function (data) {
this.data = data;
};
var ErrorResponse = function (response) {
if (response.responseJSON) {
this.message = response.responseJSON.Message;
this.validationErrors = response.responseJSON.ValidationErrors;
var ErrorResponse = function (response, identityResponse) {
var errorModel = null;
if (identityResponse && identityResponse === true && response.responseJSON && response.responseJSON.ErrorModel) {
errorModel = response.responseJSON.ErrorModel;
}
else if (response.responseJSON) {
errorModel = response.responseJSON;
}
if (errorModel) {
this.message = errorModel.Message;
this.validationErrors = errorModel.ValidationErrors;
}
this.statusCode = response.status;
};
@ -71,8 +80,27 @@ var CipherHistoryResponse = function (response) {
var revised = response.Revised;
for (var i = 0; i < revised.length; i++) {
revised.push(new CipherResponse(revised[i]));
this.revised.push(new CipherResponse(revised[i]));
}
this.deleted = response.Deleted;
};
var DomainsResponse = function (response) {
var GlobalDomainResponse = function (response) {
this.type = response.Type;
this.domains = response.Domains;
this.excluded = response.Excluded;
};
this.equivalentDomains = response.EquivalentDomains;
this.globalEquivalentDomains = [];
var globalEquivalentDomains = response.GlobalEquivalentDomains;
if (!globalEquivalentDomains) {
return;
}
for (var i = 0; i < globalEquivalentDomains.length; i++) {
this.globalEquivalentDomains.push(new GlobalDomainResponse(globalEquivalentDomains[i]));
}
};

View File

@ -32,19 +32,24 @@
return;
}
$scope.loginPromise = authService.logIn(model.email, model.masterPassword);
$scope.loginPromise = authService.logIn(model.email, model.masterPassword, null);
$scope.loginPromise.then(function () {
userService.isTwoFactorAuthenticated(function (isTwoFactorAuthenticated) {
if (isTwoFactorAuthenticated) {
$analytics.eventTrack('Logged In To Two-step');
$state.go('twoFactor', { animation: 'in-slide-left' });
}
else {
$analytics.eventTrack('Logged In');
$state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true });
}
});
$scope.loginPromise.then(function (twoFactor) {
if (twoFactor) {
$analytics.eventTrack('Logged In To Two-step');
$state.go('twoFactor', {
animation: 'in-slide-left',
email: model.email,
masterPassword: model.masterPassword
});
}
else {
$analytics.eventTrack('Logged In');
$state.go('tabs.vault', {
animation: 'in-slide-left',
syncOnLoad: true
});
}
});
};
});

View File

@ -2,12 +2,15 @@
.module('bit.accounts')
.controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService,
$analytics, i18nService) {
$analytics, i18nService, $stateParams) {
$scope.i18n = i18nService;
$scope.model = {};
utilsService.initListSectionItemListeners($(document), angular);
$('#code').focus();
var email = $stateParams.email;
var masterPassword = $stateParams.masterPassword;
$scope.loginPromise = null;
$scope.login = function (model) {
if (!model.code) {
@ -15,7 +18,7 @@
return;
}
$scope.loginPromise = authService.logInTwoFactor(model.code);
$scope.loginPromise = authService.logIn(email, masterPassword, model.code);
$scope.loginPromise.then(function () {
$analytics.eventTrack('Logged In From Two-step');
$state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true });

View File

@ -49,7 +49,7 @@
controller: 'accountsLoginTwoFactorController',
templateUrl: 'app/accounts/views/accountsLoginTwoFactor.html',
data: { authorize: false },
params: { animation: null }
params: { animation: null, email: null, masterPassword: null }
})
.state('register', {
url: '/register',
@ -190,42 +190,38 @@
params: { animation: null }
});
})
.run(function ($rootScope, userService, authService, cryptoService, tokenService, $state, constantsService, stateService) {
.run(function ($rootScope, userService, cryptoService, tokenService, $state, constantsService, stateService) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
if ($state.current.name.indexOf('tabs.') > -1 && toState.name.indexOf('tabs.') > -1) {
stateService.purgeState();
}
cryptoService.getKey(false, function (key) {
tokenService.getToken(function (token) {
userService.isAuthenticated(function (isAuthenticated) {
if (isAuthenticated) {
var obj = {};
obj[constantsService.lastActiveKey] = (new Date()).getTime();
chrome.storage.local.set(obj, function () { });
}
userService.isAuthenticated(function (isAuthenticated) {
if (isAuthenticated) {
var obj = {};
obj[constantsService.lastActiveKey] = (new Date()).getTime();
chrome.storage.local.set(obj, function () { });
}
if (!toState.data || !toState.data.authorize) {
if (isAuthenticated && !tokenService.isTokenExpired(token)) {
event.preventDefault();
if (!key) {
$state.go('lock');
}
else {
$state.go('tabs.current');
}
}
return;
}
if (!isAuthenticated || tokenService.isTokenExpired(token)) {
if (!toState.data || !toState.data.authorize) {
if (isAuthenticated && !tokenService.isTokenExpired()) {
event.preventDefault();
authService.logOut(function () {
$state.go('home');
});
if (!key) {
$state.go('lock');
}
else {
$state.go('tabs.current');
}
}
});
return;
}
if (!isAuthenticated || tokenService.isTokenExpired()) {
event.preventDefault();
chrome.runtime.sendMessage({ command: 'logout' });
}
});
});
});

View File

@ -15,7 +15,7 @@ angular
$scope.loaded = false;
$scope.$on('$viewContentLoaded', function () {
$timeout(loadVault, 0);
$timeout(loadVault, 100);
});
function loadVault() {
@ -41,17 +41,9 @@ angular
canAutofill = true;
});
var filteredLogins = [];
var loginPromise = $q.when(loginService.getAllDecrypted());
loginPromise.then(function (logins) {
for (var i = 0; i < logins.length; i++) {
if (logins[i].domain && logins[i].domain === domain) {
filteredLogins.push(logins[i]);
}
}
$q.when(loginService.getAllDecryptedForDomain(domain)).then(function (logins) {
$scope.loaded = true;
$scope.logins = filteredLogins;
$scope.logins = logins;
});
});
}

View File

@ -1,7 +1,7 @@
angular
.module('bit.global')
.controller('mainController', function ($scope, $state, authService, toastr, i18nService) {
.controller('mainController', function ($scope, $state, authService, toastr, i18nService, $analytics) {
var self = this;
self.currentYear = new Date().getFullYear();
self.animation = '';
@ -23,9 +23,12 @@ angular
else if (msg.command === 'syncStarted') {
$scope.$broadcast('syncStarted');
}
else if (msg.command === 'logout') {
else if (msg.command === 'doneLoggingOut') {
authService.logOut(function () {
toastr.warning(i18nService.loginExpired, i18nService.loggedOut);
$analytics.eventTrack('Logged Out');
if (msg.expired) {
toastr.warning(i18nService.loginExpired, i18nService.loggedOut);
}
$state.go('home');
});
}

View File

@ -1,7 +1,7 @@
angular
.module('bit.lock')
.controller('lockController', function ($scope, $state, $analytics, i18nService, authService, cryptoService, toastr,
.controller('lockController', function ($scope, $state, $analytics, i18nService, cryptoService, toastr,
userService, SweetAlert) {
$scope.i18n = i18nService;
$('#master-password').focus();
@ -15,10 +15,7 @@
cancelButtonText: i18nService.cancel
}, function (confirmed) {
if (confirmed) {
authService.logOut(function () {
$analytics.eventTrack('Logged Out');
$state.go('home');
});
chrome.runtime.sendMessage({ command: 'logout' });
}
});
};

View File

@ -2,91 +2,50 @@
.module('bit.services')
.factory('authService', function (cryptoService, apiService, userService, tokenService, $q, $rootScope, loginService,
folderService) {
folderService, settingsService, syncService, appIdService, utilsService) {
var _service = {};
_service.logIn = function (email, masterPassword) {
_service.logIn = function (email, masterPassword, twoFactorToken) {
email = email.toLowerCase();
var key = cryptoService.makeKey(masterPassword, email);
var deferred = $q.defer();
cryptoService.hashPassword(masterPassword, key, function (hashedPassword) {
var request = new TokenRequest(email, hashedPassword);
appIdService.getAppId(function (appId) {
var deviceRequest = new DeviceRequest(appId, utilsService);
var request = new TokenRequest(email, hashedPassword, twoFactorToken, deviceRequest);
apiService.postToken(request, function (response) {
if (!response || !response.token) {
return;
}
apiService.postIdentityToken(request, function (response) {
// success
if (!response || !response.accessToken) {
return;
}
tokenService.setToken(response.token, function () {
cryptoService.setKey(key, function () {
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);
}
});
});
});
}, function (error) {
deferred.reject(error);
});
});
return deferred.promise;
};
_service.logInTwoFactor = function (code) {
var request = new TokenTwoFactorRequest(code.replace(' ', ''));
var deferred = $q.defer();
apiService.postTokenTwoFactor(request, function (response) {
if (!response || !response.token) {
deferred.reject();
return;
}
tokenService.setToken(response.token, function () {
userService.setUserId(response.profile.id, function () {
userService.setEmail(response.profile.email, function () {
chrome.runtime.sendMessage({ command: 'loggedIn' });
deferred.resolve(response);
});
});
});
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
};
_service.logOut = function (callback) {
userService.getUserId(function (userId) {
tokenService.clearToken(function () {
cryptoService.clearKey(function () {
cryptoService.clearKeyHash(function () {
userService.clearUserId(function () {
userService.clearEmail(function () {
loginService.clear(userId, function () {
folderService.clear(userId, function () {
$rootScope.vaultLogins = null;
$rootScope.vaultFolders = null;
chrome.runtime.sendMessage({ command: 'loggedOut' });
callback();
});
tokenService.setTokens(response.accessToken, response.refreshToken, function () {
cryptoService.setKey(key, function () {
cryptoService.setKeyHash(hashedPassword, function () {
userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(), function () {
chrome.runtime.sendMessage({ command: 'loggedIn' });
deferred.resolve(false);
});
});
});
});
}, function () {
// two factor required
deferred.resolve(true);
}, function (error) {
// error
deferred.reject(error);
});
});
});
return deferred.promise;
};
_service.logOut = function (callback) {
$rootScope.vaultLogins = null;
$rootScope.vaultFolders = null;
callback();
};
return _service;

View File

@ -42,4 +42,7 @@
})
.factory('constantsService', function () {
return chrome.extension.getBackgroundPage().constantsService;
})
.factory('settingsService', function () {
return chrome.extension.getBackgroundPage().settingsService;
});

View File

@ -1,7 +1,7 @@
angular
.module('bit.settings')
.controller('settingsController', function ($scope, authService, $state, SweetAlert, utilsService, $analytics,
.controller('settingsController', function ($scope, $state, SweetAlert, utilsService, $analytics,
i18nService, constantsService, cryptoService) {
utilsService.initListSectionItemListeners($(document), angular);
$scope.lockOption = '';
@ -38,10 +38,7 @@
}, function (confirmed) {
if (confirmed) {
cryptoService.toggleKey(function () { });
authService.logOut(function () {
$analytics.eventTrack('Logged Out');
$state.go('home');
});
chrome.runtime.sendMessage({ command: 'logout' });
}
});
}
@ -58,10 +55,7 @@
cancelButtonText: i18nService.cancel
}, function (confirmed) {
if (confirmed) {
authService.logOut(function () {
$analytics.eventTrack('Logged Out');
$state.go('home');
});
chrome.runtime.sendMessage({ command: 'logout' });
}
});
};

View File

@ -9,7 +9,7 @@
$scope.sync = function () {
$scope.loading = true;
syncService.fullSync(function () {
syncService.fullSync(true, function () {
$analytics.eventTrack('Synced Full');
$scope.loading = false;
toastr.success(i18nService.syncingComplete);

View File

@ -13,7 +13,7 @@
if (syncOnLoad) {
$scope.$on('$viewContentLoaded', function () {
$timeout(function () {
syncService.fullSync(function () { });
syncService.fullSync(true, function () { });
}, 0);
});
}

View File

@ -28,8 +28,8 @@
<div class="list">
<div class="list-grouped" ng-repeat="folder in vaultFolders | orderBy: folderSort" ng-show="vaultFolderLogins.length">
<div class="list-grouped-header">
<i class="fa fa-folder-open"></i> {{folder.name}}
<small>{{vaultFolderLogins.length}}</small>
<i class="fa fa-folder-open"></i> {{folder.name}}
</div>
<a href="javascript:void(0)" ng-click="viewLogin(login)"
class="list-grouped-item condensed" title="{{i18n.edit}} {{login.name}}"

View File

@ -1,6 +1,10 @@
function ApiService(tokenService) {
function ApiService(tokenService, appIdService, utilsService, logoutCallback) {
//this.baseUrl = 'http://localhost:4000';
this.baseUrl = 'https://api.bitwarden.com';
this.tokenService = tokenService;
this.logoutCallback = logoutCallback;
this.appIdService = appIdService;
this.utilsService = utilsService;
initApiService();
};
@ -8,58 +12,67 @@ function ApiService(tokenService) {
function initApiService() {
// Auth APIs
ApiService.prototype.postToken = function (tokenRequest, success, error) {
ApiService.prototype.postIdentityToken = function (tokenRequest, success, successWithTwoFactor, error) {
var self = this;
$.ajax({
type: 'POST',
url: self.baseUrl + '/auth/token',
data: JSON.stringify(tokenRequest),
contentType: 'application/json; charset=utf-8',
url: self.baseUrl + '/connect/token',
data: tokenRequest.toIdentityToken(),
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
dataType: 'json',
success: function (response) {
success(new TokenResponse(response));
success(new IdentityTokenResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
};
ApiService.prototype.postTokenTwoFactor = function (twoFactorTokenRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/auth/token/two-factor?access_token=' + token,
data: JSON.stringify(twoFactorTokenRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (response) {
success(new TokenResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
if (jqXHR.responseJSON && jqXHR.responseJSON.TwoFactorProviders &&
jqXHR.responseJSON.TwoFactorProviders.length) {
successWithTwoFactor();
}
});
else {
error(new ErrorResponse(jqXHR, true));
}
}
});
};
// Account APIs
ApiService.prototype.getProfile = function (success, error) {
ApiService.prototype.getAccountRevisionDate = function (success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/accounts/profile?access_token=' + token,
url: self.baseUrl + '/accounts/revision-date?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(response);
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.getProfile = function (success, error) {
var self = this;
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/accounts/profile?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new ProfileResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
@ -75,7 +88,7 @@ function initApiService() {
success();
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
};
@ -92,36 +105,59 @@ function initApiService() {
success();
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
};
// Settings APIs
ApiService.prototype.getIncludedDomains = function (success, error) {
var self = this;
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/settings/domains?excluded=false&access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new DomainsResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
// Login APIs
ApiService.prototype.getLogin = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token,
url: self.baseUrl + '/sites/' + id + '?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new LoginResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.postLogin = function (loginRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/sites?access_token=' + token,
url: self.baseUrl + '/sites?access_token2=' + token,
data: JSON.stringify(loginRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -129,18 +165,20 @@ function initApiService() {
success(new LoginResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.putLogin = function (id, loginRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token,
url: self.baseUrl + '/sites/' + id + '?access_token2=' + token,
data: JSON.stringify(loginRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -148,9 +186,11 @@ function initApiService() {
success(new LoginResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
@ -158,27 +198,29 @@ function initApiService() {
ApiService.prototype.getFolder = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token,
url: self.baseUrl + '/folders/' + id + '?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new FolderResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.postFolder = function (folderRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/folders?access_token=' + token,
url: self.baseUrl + '/folders?access_token2=' + token,
data: JSON.stringify(folderRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -186,18 +228,20 @@ function initApiService() {
success(new FolderResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.putFolder = function (id, folderRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token,
url: self.baseUrl + '/folders/' + id + '?access_token2=' + token,
data: JSON.stringify(folderRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -205,9 +249,11 @@ function initApiService() {
success(new FolderResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
@ -215,27 +261,29 @@ function initApiService() {
ApiService.prototype.getCipher = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/ciphers/' + id + '?access_token=' + token,
url: self.baseUrl + '/ciphers/' + id + '?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new CipherResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.getCiphers = function (success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/ciphers?access_token=' + token,
url: self.baseUrl + '/ciphers?access_token2=' + token,
dataType: 'json',
success: function (response) {
var data = [];
@ -246,37 +294,124 @@ function initApiService() {
success(new ListResponse(data));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.deleteCipher = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/ciphers/' + id + '/delete?access_token=' + token,
url: self.baseUrl + '/ciphers/' + id + '/delete?access_token2=' + token,
dataType: 'text',
success: function (response) {
success();
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
// Helpers
function handleError(errorCallback, jqXHR, textStatus, errorThrown) {
if (jqXHR.status === 401 || jqXHR.status === 403) {
chrome.runtime.sendMessage({ command: 'logout' });
function handleError(errorCallback, jqXHR, tokenError, self) {
if (tokenError || jqXHR.status === 401 || jqXHR.status === 403) {
if (self && self.logoutCallback) {
self.logoutCallback(true, function () { })
}
else {
chrome.runtime.sendMessage({ command: 'logout', expired: true });
}
return;
}
errorCallback(new ErrorResponse(jqXHR));
}
function handleTokenState(self) {
var deferred = Q.defer();
self.tokenService.getAuthBearer(function (authBearer) {
self.tokenService.getToken(function (accessToken) {
// handle transferring from old auth bearer
if (authBearer && !accessToken) {
self.appIdService.getAppId(function (appId) {
postConnectToken(self, {
grant_type: 'password',
oldAuthBearer: authBearer,
username: 'abcdefgh', // has to be something
password: 'abcdefgh', // has to be something
scope: 'api offline_access',
client_id: 'browser',
deviceIdentifier: appId,
deviceType: self.utilsService.getDeviceType(),
deviceName: self.utilsService.getBrowser()
}, function (token) {
self.tokenService.clearAuthBearer(function () {
tokenService.setTokens(token.accessToken, token.refreshToken, function () {
deferred.resolve(token.accessToken);
});
});
}, function (jqXHR) {
deferred.reject(jqXHR);
});
});
} // handle token refresh
else if (self.tokenService.tokenNeedsRefresh()) {
self.tokenService.getRefreshToken(function (refreshToken) {
if (!refreshToken || refreshToken === '') {
deferred.reject();
return;
}
postConnectToken(self, {
grant_type: 'refresh_token',
client_id: 'browser',
refresh_token: refreshToken
}, function (token) {
tokenService.setTokens(token.accessToken, token.refreshToken, function () {
deferred.resolve(token.accessToken);
});
}, function (jqXHR) {
deferred.reject(jqXHR);
});
});
}
else {
if (authBearer) {
self.tokenService.clearAuthBearer(function () { });
}
deferred.resolve(accessToken);
}
});
});
return deferred.promise
}
function postConnectToken(self, data, success, error) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/connect/token',
data: data,
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
dataType: 'json',
success: function (response) {
success(new IdentityTokenResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
error(jqXHR);
}
});
}
};

View File

@ -42,11 +42,11 @@ function initAutofill() {
passwords.push(pf);
if (fillUsername) {
username = findUsernameField(pageDetails, pf, false);
username = findUsernameField(pageDetails, pf, false, false);
if (!username) {
// not able to find any viewable username fields. maybe there are some "hidden" ones?
username = findUsernameField(pageDetails, pf, true);
username = findUsernameField(pageDetails, pf, true, false);
}
if (username) {
@ -64,11 +64,11 @@ function initAutofill() {
passwords.push(pf);
if (fillUsername && pf.elementNumber > 0) {
username = findUsernameFieldWithoutForm(pageDetails, pf, false);
username = findUsernameField(pageDetails, pf, false, true);
if (!username) {
// not able to find any viewable username fields. maybe there are some "hidden" ones?
username = findUsernameFieldWithoutForm(pageDetails, pf, true);
username = findUsernameField(pageDetails, pf, true, true);
}
if (username) {
@ -109,10 +109,10 @@ function initAutofill() {
for (var i = 0; i < passwordFields.length; i++) {
var pf = passwordFields[i];
if (formKey === pf.form) {
var uf = findUsernameField(pageDetails, pf, false);
var uf = findUsernameField(pageDetails, pf, false, false);
if (!uf) {
// not able to find any viewable username fields. maybe there are some "hidden" ones?
uf = findUsernameField(pageDetails, pf, true);
uf = findUsernameField(pageDetails, pf, true, false);
}
formData.push({
@ -140,28 +140,16 @@ function initAutofill() {
return arr;
}
function findUsernameField(pageDetails, passwordField, canBeHidden) {
for (var i = 0; i < pageDetails.fields.length; i++) {
var f = pageDetails.fields[i];
if (f.form === passwordField.form && (canBeHidden || f.viewable)
&& (f.type === 'text' || f.type === 'email' || f.type === 'tel')
&& f.elementNumber < passwordField.elementNumber) {
return f;
}
}
return null;
}
function findUsernameFieldWithoutForm(pageDetails, passwordField, canBeHidden) {
function findUsernameField(pageDetails, passwordField, canBeHidden, withoutForm) {
var usernameField = null;
for (var i = 0; i < pageDetails.fields.length; i++) {
var f = pageDetails.fields[i];
if (f.elementNumber > passwordField.elementNumber) {
if (f.elementNumber >= passwordField.elementNumber) {
break;
}
if ((canBeHidden || f.viewable) && (f.type === 'text' || f.type === 'email' || f.type === 'tel')) {
if ((withoutForm || f.form === passwordField.form) && (canBeHidden || f.viewable) &&
(f.type === 'text' || f.type === 'email' || f.type === 'tel')) {
usernameField = f;
}
}

View File

@ -8,10 +8,8 @@ function initCryptoService() {
_b64Key,
_keyHash,
_b64KeyHash,
_aes,
_aesWithMac;
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
_encKey,
_macKey;
CryptoService.prototype.setKey = function (key, callback) {
if (!callback || typeof callback !== 'function') {
@ -29,7 +27,7 @@ function initCryptoService() {
}
chrome.storage.local.set({
'key': sjcl.codec.base64.fromBits(key)
'key': forge.util.encode64(key)
}, function () {
callback();
});
@ -41,7 +39,7 @@ function initCryptoService() {
throw 'callback function required';
}
_keyHash = sjcl.codec.base64.toBits(keyHash);
_keyHash = forge.util.encode64(keyHash);
chrome.storage.local.set({
'keyHash': keyHash
@ -64,7 +62,7 @@ function initCryptoService() {
return;
}
else if (b64 && b64 === true && _key && !_b64Key) {
_b64Key = sjcl.codec.base64.fromBits(_key);
_b64Key = forge.util.encode64(_key);
callback(_b64Key);
return;
}
@ -79,7 +77,7 @@ function initCryptoService() {
chrome.storage.local.get('key', function (obj) {
if (obj && obj.key) {
_key = sjcl.codec.base64.toBits(obj.key);
_key = forge.util.decode64(obj.key);
if (b64 && b64 === true) {
_b64Key = obj.key;
@ -98,8 +96,14 @@ function initCryptoService() {
throw 'callback function required';
}
if (_encKey) {
callback(_encKey);
}
this.getKey(false, function (key) {
callback(key.slice(0, 4));
var buffer = forge.util.createBuffer(key);
_encKey = buffer.getBytes(16);
callback(_encKey);
});
};
@ -108,8 +112,15 @@ function initCryptoService() {
throw 'callback function required';
}
if (_macKey) {
callback(_macKey);
}
this.getKey(false, function (key) {
callback(key.slice(4));
var buffer = forge.util.createBuffer(key);
buffer.getBytes(16);
_macKey = buffer.getBytes(16);
callback(_macKey);
});
};
@ -126,14 +137,14 @@ function initCryptoService() {
return;
}
else if (b64 && b64 === true && _keyHash && !_b64KeyHash) {
_b64KeyHash = sjcl.codec.base64.fromBits(_keyHash);
_b64KeyHash = forge.util.encode64(_keyHash);
callback(_b64KeyHash);
return;
}
chrome.storage.local.get('keyHash', function (obj) {
if (obj && obj.keyHash) {
_keyHash = sjcl.codec.base64.toBits(obj.keyHash);
_keyHash = forge.util.decode64(obj.keyHash);
if (b64 && b64 === true) {
_b64KeyHash = obj.keyHash;
@ -151,7 +162,7 @@ function initCryptoService() {
throw 'callback function required';
}
_key = _b64Key = _aes = _aesWithMac = null;
_key = _b64Key = _macKey = _encKey = null;
chrome.storage.local.remove('key', function () {
callback();
});
@ -196,10 +207,10 @@ function initCryptoService() {
};
CryptoService.prototype.makeKey = function (password, salt, b64) {
var key = sjcl.misc.pbkdf2(password, salt, 5000, 256, null);
var key = forge.pbkdf2(password, salt, 5000, 256 / 8, 'sha256');
if (b64 && b64 === true) {
return sjcl.codec.base64.fromBits(key);
return forge.util.encode64(key);
}
return key;
@ -215,36 +226,8 @@ function initCryptoService() {
throw 'Invalid parameters.';
}
var hashBits = sjcl.misc.pbkdf2(key, password, 1, 256, null);
callback(sjcl.codec.base64.fromBits(hashBits));
});
};
CryptoService.prototype.getAes = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
this.getKey(false, function (key) {
if (!_aes && key) {
_aes = new sjcl.cipher.aes(key);
}
callback(_aes);
});
};
CryptoService.prototype.getAesWithMac = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
this.getEncKey(function (encKey) {
if (!_aesWithMac && encKey) {
_aesWithMac = new sjcl.cipher.aes(encKey);
}
callback(_aesWithMac);
var hashBits = forge.pbkdf2(key, password, 1, 256 / 8, 'sha256');
callback(forge.util.encode64(hashBits));
});
};
@ -266,22 +249,21 @@ function initCryptoService() {
// TODO: Turn on whenever ready to support encrypt-then-mac
var encKey = false ? theEncKey : key;
var response = {};
var params = {
mode: 'cbc',
iv: sjcl.random.randomWords(4, 10)
};
var ctJson = sjcl.encrypt(encKey, plaintextValue, params, response);
var ct = ctJson.match(/"ct":"([^"]*)"/)[1];
var iv = sjcl.codec.base64.fromBits(response.iv);
var buffer = forge.util.createBuffer(plaintextValue, 'utf8');
var ivBytes = forge.random.getBytesSync(16);
var cipher = forge.cipher.createCipher('AES-CBC', encKey);
cipher.start({ iv: ivBytes });
cipher.update(buffer);
cipher.finish();
var iv = forge.util.encode64(ivBytes);
var ctBytes = cipher.output.getBytes();
var ct = forge.util.encode64(ctBytes);
var cipherString = iv + '|' + ct;
// TODO: Turn on whenever ready to support encrypt-then-mac
if (false) {
var mac = computeMac(ct, response.iv, macKey);
var mac = computeMac(ctBytes, ivBytes, macKey);
cipherString = cipherString + '|' + mac;
}
@ -303,31 +285,32 @@ function initCryptoService() {
throw 'cannot decrypt nothing';
}
self.getMacKey(function (macKey) {
if (!macKey) {
throw 'MAC key unavailable.';
}
self.getAes(function (aes) {
self.getAesWithMac(function (aesWithMac) {
if (!aes || !aesWithMac) {
throw 'AES encryption unavailable.';
self.getKey(false, function (key) {
self.getEncKey(function (theEncKey) {
self.getMacKey(function (macKey) {
if (!macKey) {
throw 'MAC key unavailable.';
}
var ivBits = sjcl.codec.base64.toBits(cipherString.initializationVector);
var ctBits = sjcl.codec.base64.toBits(cipherString.cipherText);
var ivBytes = forge.util.decode64(cipherString.initializationVector);
var ctBytes = forge.util.decode64(cipherString.cipherText);
var computedMac = null;
if (cipherString.mac) {
computedMac = computeMac(ctBits, ivBits, macKey);
computedMac = computeMac(ctBytes, ivBytes, macKey);
if (computedMac !== cipherString.mac) {
console.error('MAC failed.');
deferred.reject('MAC failed.');
}
}
var decBits = sjcl.mode.cbc.decrypt(computedMac ? aesWithMac : aes, ctBits, ivBits, null);
var decValue = sjcl.codec.utf8String.fromBits(decBits);
var ctBuffer = forge.util.createBuffer(ctBytes);
var decipher = forge.cipher.createDecipher('AES-CBC', computedMac ? theEncKey : key);
decipher.start({ iv: ivBytes });
decipher.update(ctBuffer);
decipher.finish();
var decValue = decipher.output.toString('utf8');
deferred.resolve(decValue);
});
});
@ -337,16 +320,10 @@ function initCryptoService() {
};
function computeMac(ct, iv, macKey) {
if (typeof ct === 'string') {
ct = sjcl.codec.base64.toBits(ct);
}
if (typeof iv === 'string') {
iv = sjcl.codec.base64.toBits(iv);
}
var hmac = new sjcl.misc.hmac(macKey, sjcl.hash.sha256);
var bits = iv.concat(ct);
var mac = hmac.encrypt(bits);
return sjcl.codec.base64.fromBits(mac);
var hmac = forge.hmac.create();
hmac.start('sha256', macKey);
hmac.update(iv + ct);
var mac = hmac.digest();
return forge.util.encode64(mac.getBytes());
}
};

View File

@ -1,7 +1,8 @@
function LoginService(cryptoService, userService, apiService) {
function LoginService(cryptoService, userService, apiService, settingsService) {
this.cryptoService = cryptoService;
this.userService = userService;
this.apiService = apiService;
this.settingsService = settingsService;
this.decryptedLoginCache = null;
initLoginService();
@ -133,10 +134,31 @@ function initLoginService() {
LoginService.prototype.getAllDecryptedForDomain = function (domain) {
var self = this;
return self.getAllDecrypted().then(function (logins) {
var eqDomainsPromise = self.settingsService.getEquivalentDomains().then(function (eqDomains) {
var matchingDomains = [];
for (var i = 0; i < eqDomains.length; i++) {
if (eqDomains[i].length && eqDomains[i].indexOf(domain) >= 0) {
matchingDomains = matchingDomains.concat(eqDomains[i]);
}
}
if (!matchingDomains.length) {
matchingDomains.push(domain);
}
return matchingDomains;
});
var loginsPromise = self.getAllDecrypted().then(function (logins) {
return logins;
});
return Q.all([eqDomainsPromise, loginsPromise]).then(function (result) {
var matchingDomains = result[0];
var logins = result[1];
var loginsToReturn = [];
for (var i = 0; i < logins.length; i++) {
if (logins[i].domain === domain) {
if (logins[i].domain && matchingDomains.indexOf(logins[i].domain) >= 0) {
loginsToReturn.push(logins[i]);
}
}

View File

@ -0,0 +1,106 @@
function SettingsService(userService) {
this.userService = userService;
this.settingsCache = null;
initSettingsService();
};
function initSettingsService() {
SettingsService.prototype.clearCache = function () {
this.settingsCache = null
};
SettingsService.prototype.getSettings = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
if (self.settingsCache) {
callback(self.settingsCache);
return;
}
this.userService.getUserId(function (userId) {
var key = 'settings_' + userId;
chrome.storage.local.get(key, function (obj) {
self.settingsCache = obj[key];
callback(self.settingsCache);
});
});
};
SettingsService.prototype.getEquivalentDomains = function (callback) {
var deferred = Q.defer();
getSettingsKey(this, 'equivalentDomains', function (domains) {
deferred.resolve(domains);
});
return deferred.promise;
};
function getSettingsKey(self, key, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
self.getSettings(function (settings) {
if (settings && settings[key]) {
callback(settings[key]);
return;
}
callback(null);
});
}
SettingsService.prototype.setEquivalentDomains = function (equivalentDomains, callback) {
setSettingsKey(this, 'equivalentDomains', equivalentDomains, callback);
};
function setSettingsKey(self, key, value, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
self.userService.getUserId(function (userId) {
var settingsKey = 'settings_' + userId;
self.getSettings(function (settings) {
if (!settings) {
settings = {};
}
settings[key] = value;
var obj = {};
obj[settingsKey] = settings;
chrome.storage.local.set(obj, function () {
self.settingsCache = settings;
callback();
});
});
});
}
SettingsService.prototype.clear = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
this.userService.getUserId(function (userId) {
chrome.storage.local.remove('settings_' + userId, function () {
self.settingsCache = null;
callback();
});
});
};
function handleError(error, deferred) {
deferred.reject(error);
}
};

View File

@ -1,15 +1,16 @@
function SyncService(loginService, folderService, userService, apiService) {
function SyncService(loginService, folderService, userService, apiService, settingsService) {
this.loginService = loginService;
this.folderService = folderService;
this.userService = userService;
this.apiService = apiService;
this.settingsService = settingsService;
this.syncInProgress = false;
initSyncService();
};
function initSyncService() {
SyncService.prototype.fullSync = function (callback) {
SyncService.prototype.fullSync = function (forceSync, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
@ -26,40 +27,85 @@ function initSyncService() {
self.userService.getUserId(function (userId) {
var now = new Date();
var ciphers = self.apiService.getCiphers(function (response) {
var logins = {};
var folders = {};
for (var i = 0; i < response.data.length; i++) {
var data = response.data[i];
if (data.type === 1) {
logins[data.id] = new LoginData(data, userId);
}
else if (data.type === 0) {
folders[data.id] = new FolderData(data, userId);
}
needsSyncing(self, forceSync, function (needsSync) {
if (!needsSync) {
self.syncCompleted(false);
callback(false);
return;
}
self.folderService.replace(folders, function () {
self.loginService.replace(logins, function () {
self.setLastSync(now, function () {
self.syncCompleted(true);
callback(true);
});
syncVault(userId).then(function () {
return syncSettings(userId);
}).then(function () {
self.setLastSync(new Date(), function () {
self.syncCompleted(true);
callback(true);
});
}, function () {
self.syncCompleted(false);
callback(false);
});
}, handleError);
});
});
});
};
SyncService.prototype.incrementalSync = function (callback) {
function needsSyncing(self, forceSync, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
// TODO
};
if (forceSync) {
callback(true);
return;
}
self.getLastSync(function (lastSync) {
var now = new Date();
self.apiService.getAccountRevisionDate(function (response) {
var accountRevisionDate = new Date(response);
if (lastSync && accountRevisionDate <= lastSync) {
callback(false);
return;
}
callback(true);
});
});
}
function syncVault(userId) {
var deferred = Q.defer();
var self = this;
self.apiService.getCiphers(function (response) {
var logins = {};
var folders = {};
for (var i = 0; i < response.data.length; i++) {
var data = response.data[i];
if (data.type === 1) {
logins[data.id] = new LoginData(data, userId);
}
else if (data.type === 0) {
folders[data.id] = new FolderData(data, userId);
}
}
self.folderService.replace(folders, function () {
self.loginService.replace(logins, function () {
deferred.resolve();
return;
});
});
}, function () {
deferred.reject();
return;
});
return deferred.promise
}
function syncFolders(serverFolders, callback) {
var self = this;
@ -129,6 +175,35 @@ function initSyncService() {
});
}
function syncSettings(userId) {
var deferred = Q.defer();
var self = this;
var ciphers = self.apiService.getIncludedDomains(function (response) {
var eqDomains = [];
if (response && response.equivalentDomains) {
eqDomains = eqDomains.concat(response.equivalentDomains);
}
if (response && response.globalEquivalentDomains) {
for (var i = 0; i < response.globalEquivalentDomains.length; i++) {
if (response.globalEquivalentDomains[i].domains.length) {
eqDomains.push(response.globalEquivalentDomains[i].domains);
}
}
}
self.settingsService.setEquivalentDomains(eqDomains, function () {
deferred.resolve();
return;
});
}, function () {
deferred.reject();
return;
});
return deferred.promise;
};
SyncService.prototype.getLastSync = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
@ -153,10 +228,6 @@ function initSyncService() {
throw 'callback function required';
}
if (!(date instanceof Date)) {
throw 'date must be a Date object';
}
this.userService.getUserId(function (userId) {
var lastSyncKey = 'lastSync_' + userId;
@ -178,9 +249,4 @@ function initSyncService() {
this.syncInProgress = false;
chrome.runtime.sendMessage({ command: 'syncCompleted', successfully: successfully });
};
function handleError() {
syncCompleted(false);
// TODO: check for unauth or forbidden and logout
}
};

View File

@ -3,7 +3,23 @@
};
function initTokenService() {
var _token;
var _token,
_authBearer,
_decodedToken,
_refreshToken;
TokenService.prototype.setTokens = function (accessToken, refreshToken, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
self.setToken(accessToken, function () {
self.setRefreshToken(refreshToken, function () {
callback();
});
});
};
TokenService.prototype.setToken = function (token, callback) {
if (!callback || typeof callback !== 'function') {
@ -11,8 +27,9 @@ function initTokenService() {
}
_token = token;
_decodedToken = null;
chrome.storage.local.set({
'authBearer': token
'accessToken': token
}, function () {
callback();
});
@ -27,48 +44,120 @@ function initTokenService() {
return callback(_token);
}
chrome.storage.local.get('authBearer', function (obj) {
if (obj && obj.authBearer) {
_token = obj.authBearer;
chrome.storage.local.get('accessToken', function (obj) {
if (obj && obj.accessToken) {
_token = obj.accessToken;
}
return callback(_token);
});
};
TokenService.prototype.getAuthBearer = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
if (_authBearer) {
return callback(_authBearer);
}
chrome.storage.local.get('authBearer', function (obj) {
if (obj && obj.authBearer) {
_authBearer = obj.authBearer;
}
return callback(_authBearer);
});
};
TokenService.prototype.setRefreshToken = function (refreshToken, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_refreshToken = refreshToken;
chrome.storage.local.set({
'refreshToken': refreshToken
}, function () {
callback();
});
};
TokenService.prototype.getRefreshToken = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
if (_refreshToken) {
return callback(_refreshToken);
}
chrome.storage.local.get('refreshToken', function (obj) {
if (obj && obj.refreshToken) {
_refreshToken = obj.refreshToken;
}
return callback(_refreshToken);
});
};
TokenService.prototype.clearAuthBearer = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_authBearer = null;
chrome.storage.local.remove('authBearer', function () {
callback();
});
};
TokenService.prototype.clearToken = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_token = null;
_token = _decodedToken = _refreshToken = _authBearer = null;
chrome.storage.local.remove('authBearer', function () {
callback();
chrome.storage.local.remove('accessToken', function () {
chrome.storage.local.remove('refreshToken', function () {
callback();
});
});
});
};
// jwthelper methods
// ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js
TokenService.prototype.decodeToken = function (token) {
var parts = token.split('.');
TokenService.prototype.decodeToken = function () {
if (_decodedToken) {
return _decodedToken;
}
if (!_token) {
throw 'Token not found.';
}
var parts = _token.split('.');
if (parts.length !== 3) {
throw new Error('JWT must have 3 parts');
throw 'JWT must have 3 parts';
}
var decoded = urlBase64Decode(parts[1]);
if (!decoded) {
throw new Error('Cannot decode the token');
throw 'Cannot decode the token';
}
return JSON.parse(decoded);
_decodedToken = JSON.parse(decoded);
return _decodedToken;
};
TokenService.prototype.getTokenExpirationDate = function (token) {
var decoded = this.decodeToken(token);
TokenService.prototype.getTokenExpirationDate = function () {
var decoded = this.decodeToken();
if (typeof decoded.exp === "undefined") {
if (typeof decoded.exp === 'undefined') {
return null;
}
@ -78,8 +167,8 @@ function initTokenService() {
return d;
};
TokenService.prototype.isTokenExpired = function (token, offsetSeconds) {
var d = this.getTokenExpirationDate(token);
TokenService.prototype.isTokenExpired = function (offsetSeconds) {
var d = this.getTokenExpirationDate();
offsetSeconds = offsetSeconds || 0;
if (d === null) {
return false;
@ -89,6 +178,53 @@ function initTokenService() {
return !(d.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000)));
};
TokenService.prototype.tokenSecondsRemaining = function (offsetSeconds) {
var d = this.getTokenExpirationDate();
offsetSeconds = offsetSeconds || 0;
if (d === null) {
return 0;
}
var msRemaining = d.valueOf() - (new Date().valueOf() + (offsetSeconds * 1000));
return Math.round(msRemaining / 1000);
};
TokenService.prototype.tokenNeedsRefresh = function (minutes) {
minutes = minutes || 5; // default 5 minutes
var sRemaining = this.tokenSecondsRemaining();
return sRemaining < (60 * minutes);
};
TokenService.prototype.getUserId = function () {
var decoded = this.decodeToken();
if (typeof decoded.sub === 'undefined') {
throw 'No user id found';
}
return decoded.sub;
};
TokenService.prototype.getEmail = function () {
var decoded = this.decodeToken();
if (typeof decoded.email === 'undefined') {
throw 'No email found';
}
return decoded.email;
};
TokenService.prototype.getName = function () {
var decoded = this.decodeToken();
if (typeof decoded.name === 'undefined') {
throw 'No name found';
}
return decoded.name;
};
function urlBase64Decode(str) {
var output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {

View File

@ -13,16 +13,23 @@ function initUserService() {
var _userId = null,
_email = null;
UserService.prototype.setUserId = function (userId, callback) {
UserService.prototype.setUserIdAndEmail = function (userId, email, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_email = email;
var emailObj = {};
emailObj[userEmailKey] = email;
_userId = userId;
var obj = {};
obj[userIdKey] = userId;
chrome.storage.local.set(obj, function () {
callback();
var userIdObj = {};
userIdObj[userIdKey] = userId;
chrome.storage.local.set(userIdObj, function () {
chrome.storage.local.set(emailObj, function () {
callback();
});
});
};
@ -44,30 +51,6 @@ function initUserService() {
});
};
UserService.prototype.clearUserId = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_userId = null;
chrome.storage.local.remove(userIdKey, function () {
callback();
});
};
UserService.prototype.setEmail = function (email, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_email = email;
var obj = {};
obj[userEmailKey] = email;
chrome.storage.local.set(obj, function () {
callback();
});
};
UserService.prototype.getEmail = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
@ -86,14 +69,16 @@ function initUserService() {
});
};
UserService.prototype.clearEmail = function (callback) {
UserService.prototype.clearUserIdAndEmail = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_email = null;
chrome.storage.local.remove(userEmailKey, function () {
callback();
_userId = _email = null;
chrome.storage.local.remove(userIdKey, function () {
chrome.storage.local.remove(userEmailKey, function () {
callback();
});
});
};
@ -104,32 +89,16 @@ function initUserService() {
var self = this;
self.tokenService.getToken(function (token) {
if (!token) {
callback(false);
}
else {
self.getUserId(function (userId) {
callback(userId !== null);
});
}
});
};
UserService.prototype.isTwoFactorAuthenticated = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
self.tokenService.getToken(function (token) {
if (!token) {
callback(false);
}
else {
self.getUserId(function (userId) {
callback(userId === null);
});
}
self.tokenService.getAuthBearer(function (authBearer) {
if (!token && !authBearer) {
callback(false);
}
else {
self.getUserId(function (userId) {
callback(userId !== null);
});
}
});
});
};
};

View File

@ -64,6 +64,23 @@ function initUtilsService() {
return this.analyticsIdCache;
}
UtilsService.prototype.getDeviceType = function () {
if (this.isChrome()) {
return 2;
}
else if (this.isFirefox()) {
return 3;
}
else if (this.isEdge()) {
return 5;
}
else if (this.isOpera()) {
return 4;
}
return -1;
}
UtilsService.prototype.initListSectionItemListeners = function (doc, angular) {
if (!doc) {
throw 'doc parameter required';