hopefully support for realms, but some problems on citi

This commit is contained in:
Chris Karlof 2013-01-14 22:09:40 -08:00
Родитель 28486ae16c
Коммит cf78626e9a
12 изменённых файлов: 102 добавлений и 257 удалений

9
TODO
Просмотреть файл

@ -1,8 +1,12 @@
get server sync going
implement origins in capture, linking, filling, and configs
need busy indicator on login page
fix update_password in infobar_hooks.js
figure out downloadExportDataFile function (does it work, is it exposed?)
login success goes to successful install page
start capturing login url and title
seems to be race condition with citi realm (problems with capturing on accountonline.com and filling on https://creditcards.citi.com/)
get multistage working
HTTP auth capture/fill
capturing and filling lone password fields
tdameritrade may need some type like a human for lone password field on transfer page
@ -11,4 +15,3 @@ bayareacurling has annoying change password page
consider supporting grabbing username from select elements (online.citibank.com when remember me is enabled). This site also has a dynamic username box when you say log in as another user
start capturing login url and title

Просмотреть файл

@ -4,8 +4,10 @@ var CapturedCredentialStorage = function(Realms) {
function mergeCredentials(newCredentials, source) {
var oldCredentials = storage[source.id];
if (!oldCredentials || newCredentials.realm !== oldCredentials.realm) {
// TODO: revisit strict equality requirement here
if (!oldCredentials || newCredentials.origin !== oldCredentials.origin) {
storage[source.id] = newCredentials;
//console.log("Storing credentials", storage[source.id]);
return;
}
if (newCredentials.password) {
@ -20,28 +22,24 @@ var CapturedCredentialStorage = function(Realms) {
// Stores captured credentials
// Input:
// credentials: object with properties:
// usernames: An array of { <name>: <username> } key/value pairs. The <name> is a
// descriptive name for the <username>. In the common case where there is a
// single username, the <name> will often be "username".
// usernames: a username
// password: a password
// id: identifier for the credentials
// source: object with properties:
// id: identifier for the credential's source
// url: url of the credential's source
function setCredentials(credentials, source) {
credentials.domain = new Uri(source.url).host();
credentials.realm = Realms.getRealm(credentials.domain);
credentials.origin = Realms.getOriginForUri(source.url);
mergeCredentials(credentials, source);
console.log("Storing credentials", storage[source.id]);
}
function getCrendentials(credentials, source, callback) {
callback(storage[source.id]);
console.log("Getting credentials", storage[source.id]);
//console.log("Getting credentials", storage[source.id]);
}
function deleteCredentials(source) {
console.log("Deleting credentials for tab", source.id)
//console.log("Deleting credentials for tab", source.id)
delete storage[source.id]
}

Просмотреть файл

@ -1,30 +1,35 @@
var CommandHandler = function(Messaging, CapturedCredentialStorage, Realms) {
function addLogin(message, sender) {
var currentUser = Gombot.getCurrentUser();
var notificationObj = message,
tabID = sender.tab.id,
username = notificationObj.username;
var tabID = sender.tab.id,
origin = message.origin,
username = message.username,
password = message.password;
// Check to see if the user disabled password saving on this site
if (currentUser.get('disabledSites')[notificationObj.hostname] === 'all') {
if (currentUser.get('disabledSites')[origin] === 'all') {
return;
}
notificationObj.type = 'password_observed';
message.type = 'password_observed';
// Look for passwords in use on the current site
var loginForSameUsername = currentUser.get('logins').find(function(login) {
return login.get('hostname') === hostname &&
login.get('username') === username;
var loginForSavedUsername = currentUser.get('logins').find(function(login) {
// TODO, FIXME: This assumes non-user-edited realms, meaning each realm will be an
// array of 1 non-wildcarded realm
return Realms.isOriginMemberOfRealm(origin,
Realms.getRealmForOrigin(login.get('origins')[0])) &&
login.get('username') === username;
});
if (loginForSameUsername) {
if (loginForSameUsername.get("password") === password) {
if (loginForSavedUsername) {
if (loginForSavedUsername.get("password") === password) {
// We're just logging into a site with an existing login. Bail.
return;
}
else {
// Prompt user to update password
notificationObj.type = 'update_password';
message.type = 'update_password';
// If the existing login stored for this site was PIN locked,
// make sure this new one will be also.
notificationObj.pinLocked = loginForSameUsername.get("pinLocked");
message.pinLocked = loginForSameUsername.get("pinLocked");
}
}
if (currentUser && currentUser.keys) {
@ -32,7 +37,7 @@ var CommandHandler = function(Messaging, CapturedCredentialStorage, Realms) {
displayInfobar({
notify: true,
tabID: tabID,
notification: notificationObj
notification: message
});
} else {
displayInfobar({
@ -64,13 +69,13 @@ var CommandHandler = function(Messaging, CapturedCredentialStorage, Realms) {
}
function getSavedCredentials(message, sender, callback) {
var hostname = Realms.getRealm(sender.tab.url);
var pageOrigin = Realms.getOriginForUri(sender.tab.url);
var currentUser = Gombot.getCurrentUser();
var logins = [];
if (currentUser) {
logins = currentUser.get('logins').filter(function(x) {
return x.hostname === hostname;
});
logins = currentUser.get('logins').filter(function(login) {
return Realms.isOriginMemberOfRealm(pageOrigin, Realms.getRealmForOrigin(login.get('origins')[0]));
});
}
callback(logins);
// Chrome requires that we return true if we plan to call a callback
@ -93,6 +98,7 @@ var CommandHandler = function(Messaging, CapturedCredentialStorage, Realms) {
return true;
}
// probably will need to tweak this
function getSiteConfig(message, sender, callback) {
callback(Gombot.SiteConfigs[Gombot.TldService.getDomain(sender.tab.url)] || {});
}

Просмотреть файл

@ -10,7 +10,7 @@ function formatStoredLogin(login) {
return {
username: login.username,
password: login.password,
hostname: login.hostname,
origins: [ login.origin ],
// Fields that may be missing
title: login['title'] || '',
@ -20,8 +20,6 @@ function formatStoredLogin(login) {
};
}
// Gombot.getCurrentUser().get('logins').filter(function(x) { return x.hostname == 'facebook.com'; } )
var infobarHooks = {
'password_observed': function (notificationObj,infobarResponse) {
console.log(notificationObj);
@ -43,8 +41,8 @@ var infobarHooks = {
break;
case 'never_for_this_site':
var hostname = formattedLoginObj.hostname;
currentUser.get('disabledSites')[hostname] = 'all';
var origin = formattedLoginObj.origins[0];
currentUser.get('disabledSites')[origin] = 'all';
currentUser.save();
break;
@ -71,6 +69,7 @@ var infobarHooks = {
}
}
},
// TODO: fix this
'update_password': function(notificationObj,infobarResponse) {
User.Logins.add(notificationObj.notification);
}

Просмотреть файл

@ -26,203 +26,6 @@ var LocalStorage = function() {
}
};
// var User = (function(Storage){
// // In-memory storage for user data that gets synced.
// var _logins = {};
// var _neverAsk = {};
// var _pin = null;
// // In-memory storasge for user data that's persisted to localStorage, but not synced.
// var _didFirstRun = false;
// // The key for the document in localStorage which holds all of the user
// // data synced with the server.
// const SYNC_DOCUMENT_KEY = 'gombot_user_data';
// const DATA_VERSION = 'identity.mozilla.com/gombot/v1/userData';
// // NB: remove every call to fetchStoredData and updateStoredData before commit!
// function fetchStoredData(callback) {
// var keysToFetch = [
// SYNC_DOCUMENT_KEY,
// 'did_first_run'
// ];
// chrome.storage.local.get(SYNC_DOCUMENT_KEY, function(storageObj) {
// var userData = storageObj;//[SYNC_DOCUMENT_KEY];
// if (userData === undefined) {
// userData = {
// version: DATA_VERSION,
// logins: {},
// pin: loginsLock.pin || null,
// neverAsk: {}
// };
// }
// callback(userData);
// });
// }
// // function updateStoredData(obj) {
// // var updatedObj = {};
// // updatedObj[SYNC_DOCUMENT_KEY] = obj;
// // chrome.storage.local.set(updatedObj);
// // }
// function saveToLocalStorage() {
// Storage.set(SYNC_DOCUMENT_KEY, {
// version: DATA_VERSION,
// logins: _logins || {},
// pin: _pin || null,
// neverAsk: _neverAsk || {}
// });
// Storage.set('did_first_run',_didFirstRun);
// }
// function loadFromLocalStorage() {
// fetchStoredData(function(userData) {
// _logins = userData[SYNC_DOCUMENT_KEY].logins;
// _pin = userData[SYNC_DOCUMENT_KEY].logins;
// _neverAsk = userData[SYNC_DOCUMENT_KEY].neverAsk;
// _didFirstRun = userData['did_first_run'];
// });
// }
// // Load from localStorage into memory when extension starts.
// loadFromLocalStorage();
// var userData = {};
// var loginsObj = (function() {
// function formatStoredLogin(login) {
// return {
// username: login.username,
// password: login.password,
// hostname: login.hostname,
//
// // Fields that may be missing
// title: login['title'] || '',
// url: login['url'] || '',
// pinLocked: login['pinLocked'] || false,
// supplementalInformation: login['supplementalInformation'] || {}
// };
// }
// return {
// // Save a new login object to localStorage
// add: function(newLogin) {
// var loginObj = formatStoredLogin(newLogin);
// // Filter for logins with the same username and hostname.
// var existingLoginsForHost = _logins[newLogin.hostname] || [];
// _logins[newLogin.hostname] =
// existingLoginsForHost.filter(function(_login) {
// return _login.username != loginObj.username;
// });
// _logins[newLogin.hostname].push(loginObj);
// saveToLocalStorage();
// },
// // Takes a hostname and a callback, and passes it a list of login
// // objects the user has saved for that domain.
// getForHostname: function(hostname/*,callback*/) {
// return (_logins[hostname] || []);
// },
// // Mainly for debugging purposes.
// deleteForHostname: function(hostname) {
// delete _logins[hostname];
// saveToLocalStorage();
// }
// };
// })();
// var neverAskObj = (function() {
// return {
// // Add a hostname to the list of sites for which we never
// // prompt to save passwords
// add: function(siteHostname) {
// if (!(siteHostname in _neverAsk)) {
// _neverAsk[siteHostname] = 'all';
// saveToLocalStorage();
// }
// },
// // Takes a callback, and passes it a list of domains the user
// // has elected to never save logins on.
// get: function(/*callback*/) {
// return _.keys(userData.neverAsk);
// },
// // Takes a hostname and a callback. Passes callback a boolean,
// // indicating if the user *should* be asked about logins
// // on that domain.
// checkForHostname: function(hostname/*,callback*/) {
// return !(hostname in this.get());
// }
// };
// })();
// var firstRunObj = (function(){
// return {
// wasCompleted: function() {
// // Takes a callback, and passes it a boolean indicating whether
// // or not the user has completed the first run flow.
// // Note that this will return true if the user started the first
// // run flow but did not complete it.
// return _didFirstRun;
// },
// setIfCompleted: function(firstRunFinished) {
// //setIfDidFirstRun
// // Set (or unset) the flag indicating the user has finished the first
// // run flow.
// _didFirstRun = firstRunFinished;
// saveToLocalStorage();
// }
// };
// })();
// var pinObj = (function() {
// return {
// validate: function(testPin) {
// return testPin == _pin;
// },
// get: function() {
// return _pin;
// },
// set: function(newPIN) {
// _pin = newPIN || null;
// saveToLocalStorage();
// }
// }
// });
// return {
// Logins: loginsObj,
// neverAsk: neverAskObj,
// firstRun: firstRunObj,
// PIN: pinObj
// };
// })(Gombot.Storage);
// // Takes a callback, and passes it a list of domains the user
// // has elected to never save logins on.
// function getNeverSaveOnSites(callback) {
// fetchStoredData(function(userData) {
// callback(_.keys(userData.neverAsk));
// });
// }
// // Takes a hostname and a callback, and passes it a list of login
// // objects the user has saved for that domain.
// function getLoginsForSite(hostname,callback) {
// fetchStoredData(function(userData) {
// callback(userData.logins[hostname] || []);
// });
// }
// // Mainly for debugging purposes.
// function deleteLoginsForSite(hostname) {
// fetchStoredData(function(userData) {
// delete userData[hostname];
// updateStoredData(userData);
// });
// }
// Returns a string of a comma separated value file containing the hostname, username,
// and password for each login the user has saved.
function getLoginsCSV(callback) {

Просмотреть файл

@ -24,7 +24,7 @@ var Gombot = {};
Gombot.Messaging = ChromeMessaging();
Gombot.TldService = TldService(Tld, Uri);
Gombot.SiteConfigs = SiteConfigs || {};
Gombot.Realms = Realms(Gombot.SiteConfigs, Gombot.TldService);
Gombot.Realms = Realms(Gombot.SiteConfigs, Uri);
Gombot.CapturedCredentialStorage = CapturedCredentialStorage(Gombot.Realms);
Gombot.CommandHandler = CommandHandler(Gombot.Messaging, Gombot.CapturedCredentialStorage, Gombot.Realms);
Gombot.LocalStorage = LocalStorage();
@ -44,9 +44,9 @@ Gombot.User = User(Backbone, _, Gombot.LoginCredentialCollection);
}
})(Gombot);
var usersStore = new Gombot.Storage("users", function() {
Gombot.UserCollection = UserCollection(Backbone, _, Gombot.User, usersStore);
initGombot();
new Gombot.Storage("users", function(store) {
Gombot.UserCollection = UserCollection(Backbone, _, Gombot.User, store);
initGombot();
});
function initGombot() {

Просмотреть файл

@ -3,10 +3,9 @@ var LoginCredential = function(Backbone, _) {
// LoginCredential constructor
// data is:
// {
// "hostname": "www.mozilla.com",
// "realm": "mozilla.com",
// "origins": [ "https://www.mozilla.com" ],
// "title": Mozilla,
// "url": <full url to login page>,
// "url": "https://www.mozilla.com/login",
// "password": "grëën",
// "pinLocked": false,
// "username": "gömbottest",

Просмотреть файл

@ -1,30 +1,63 @@
var Realms = function(SiteConfigs, TldService) {
var Realms = function(SiteConfigs, Uri) {
var realmMap = {};
function buildRealmMap(siteConfigs) {
var realms = Object.getOwnPropertyNames(siteConfigs);
realmMap = {};
var realms = siteConfigs.realms;
realms.forEach(function(realm) {
var domains = siteConfigs[realm].domains;
var origins = realm.origins;
// presence of a domain field indicates it's a complex realm
if (domains) {
domains.forEach(function(domain) {
realmMap[domain] = realm;
if (origins) {
origins.forEach(function(origin) {
realmMap[origin] = origins;
});
}
});
}
function getRealm(domain) {
domain = TldService.getDomain(domain);
return realmMap[domain] || domain;
function getRealmForOrigin(origin) {
var keys = Object.getOwnPropertyNames(realmMap);
for (var i=0;i<keys.length;i++) {
if (originsMatch(keys[i], origin)) {
return realmMap[keys[i]];
}
}
return [ origin ];
}
// TODO: make more general to account for wildcards in origins
function originsMatch(origin1, origin2 ) {
return origin1 === origin2;
}
function getOriginForUri(uriString) {
var uri = new Uri(uriString),
origin = uri.protocol()+"://"+uri.host(),
port = uri.port();
if (port) {
origin += ":"+port;
}
return origin;
}
// a realm is defined by an array of origins
function isOriginMemberOfRealm(origin, origins) {
var i;
for (i=0;i<origins.length;i++) {
if (originsMatch(origin,origins[i])) {
return true;
}
}
return false;
}
buildRealmMap(SiteConfigs);
return {
buildRealmMap: buildRealmMap,
getRealm: getRealm
getOriginForUri: getOriginForUri,
isOriginMemberOfRealm: isOriginMemberOfRealm,
getRealmForOrigin: getRealmForOrigin
};
}

Просмотреть файл

@ -1,2 +1,2 @@
// AUTOGENERATED FILE: edit 'site_configs.yml' instead and run 'build_site_configs.rb'
var SiteConfigs = { "bankofamerica.com": { "multiStage": true, "un": "#id" }, "vanguard.com": { "multiStage": true, "un": "#USER" }, "citi.com": { "domains": [ "citibank.com", "accountonline.com", "citicards.com" ] }, "att.com": { "fakePasswordFill": "#password" }};
var SiteConfigs = { "bankofamerica.com": { "multiStage": true, "un": "#id" }, "vanguard.com": { "multiStage": true, "un": "#USER" }, "citi.com": { "domains": [ "citibank.com", "accountonline.com", "citicards.com" ], "title": "Citi" }, "att.com": { "fakePasswordFill": "#password" }, "realms": [ { "origins": [ "https://online.citibank.com", "https://www.accountonline.com", "https://creditcards.citi.com" ], "title": "Citi" } ]};

Просмотреть файл

@ -25,6 +25,8 @@
#hulu.com:
# clickOn: "input.inactive.dummy.user"
# CONFIGS
bankofamerica.com:
multiStage: true
un: "#id"
@ -33,9 +35,11 @@ vanguard.com:
multiStage: true
un: "#USER"
citi.com: # change domains -> origins
domains: ["citibank.com", "accountonline.com", "citicards.com"]
title: "Citi"
att.com:
fakePasswordFill: "#password"
fakePasswordFill: "#password"
# REALMS
realms:
- origins: ["https://online.citibank.com", "https://www.accountonline.com", "https://creditcards.citi.com" ]
title: "Citi"

Просмотреть файл

@ -32,7 +32,7 @@ function maybePromptToSaveCapturedCredentials() {
if (!credentials || !credentials.password) return;
var loginObj = {
message: {
hostname: credentials.realm,
origin: credentials.origin,
username: credentials.username,
password: credentials.password
},

Просмотреть файл

@ -40,7 +40,7 @@ Backbone.LocalStorage = window.Store = function(name, callback) {
this.name = name;
var cb = function(store) {
this.records = (store && store.split(",")) || [];
if (callback) callback();
if (callback) callback(this);
}
this.localStorage().getItem(this.name, cb.bind(this));
};