Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Autofill password manager integration
Browse files Browse the repository at this point in the history
fix #3530
fix #5438

Auditors: @yan, @bbondy, @bridiver
  • Loading branch information
darkdh committed May 9, 2017
1 parent a3dd46f commit 2e67bc3
Show file tree
Hide file tree
Showing 19 changed files with 278 additions and 625 deletions.
16 changes: 16 additions & 0 deletions app/autofill.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,19 @@ module.exports.clearAutocompleteData = () => {
module.exports.clearAutofillData = () => {
session.defaultSession.autofill.clearAutofillData()
}

module.exports.addLogin = (form) => {
session.defaultSession.autofill.addLogin(form)
}

module.exports.updateLogin = (form) => {
session.defaultSession.autofill.updateLogin(form)
}

module.exports.removeLogin = (form) => {
session.defaultSession.autofill.removeLogin(form)
}

module.exports.clearLogins = (form) => {
session.defaultSession.autofill.clearLogins(form)
}
257 changes: 114 additions & 143 deletions app/browser/reducers/passwordManagerReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
const keytar = require('keytar')
const appConstants = require('../../../js/constants/appConstants')
const appActions = require('../../../js/actions/appActions')
const AppStore = require('../../../js/stores/appStore')
const CryptoUtil = require('../../../js/lib/cryptoUtil')
const locale = require('../../locale')
const messages = require('../../../js/constants/messages')
const siteSettings = require('../../../js/state/siteSettings')
const {getWebContents} = require('../webContentsCache')
const {makeImmutable} = require('../../common/state/immutableUtil')
const {BrowserWindow, ipcMain} = require('electron')
const Immutable = require('immutable')
const {ipcMain} = require('electron')
const autofill = require('../../autofill')

const unsafeTestMasterKey = 'c66af15fc6555ebecf7cee3a5b82c108fd3cb4b587ab0b299d28e39c79ecc708'

Expand Down Expand Up @@ -86,178 +87,148 @@ const init = () => {
passwordCallbacks[message](buttonIndex)
}
})
}

ipcMain.on(messages.DECRYPT_PASSWORD, (e, encrypted, authTag, iv, id) => {
masterKey = masterKey || getMasterKey()
if (!masterKey) {
console.log('Could not access master password; aborting')
return
}
let decrypted = CryptoUtil.decryptVerify(encrypted, authTag, masterKey, iv)
e.sender.send(messages.DECRYPTED_PASSWORD, {
id,
decrypted
const savePassword = (username, origin, tabId) => {
if (!origin) {
return
}

var message = username
? locale.translation('notificationPasswordWithUserName').replace(/{{\s*username\s*}}/, username).replace(/{{\s*origin\s*}}/, origin)
: locale.translation('notificationPassword').replace(/{{\s*origin\s*}}/, origin)

if (!(message in passwordCallbacks)) {
// Notification not shown already
appActions.showNotification({
buttons: [
{text: locale.translation('yes')},
{text: locale.translation('no')},
{text: locale.translation('neverForThisSite')}
],
options: {
persist: false,
advancedText: locale.translation('notificationPasswordSettings'),
advancedLink: 'about:passwords'
},
message
})
})
}

ipcMain.on(messages.GET_PASSWORDS, (e, origin, action) => {
const passwords = AppStore.getState().get('passwords')
if (!passwords || passwords.size === 0) {
return
}
const webContents = getWebContents(tabId)

let results = passwords.filter((password) => {
return password.get('origin') === origin && password.get('action') === action
})
passwordCallbacks[message] = (buttonIndex) => {
delete passwordCallbacks[message]
appActions.hideNotification(message)

if (results.size === 0) {
if (buttonIndex === 1) {
// don't save
return
}

masterKey = masterKey || getMasterKey()
if (!masterKey) {
console.log('Could not access master password; aborting')
if (buttonIndex === 2) {
// never save
webContents.neverSavePassword()
return
}

let isUnique = results.size === 1
results.forEach((result) => {
let password = CryptoUtil.decryptVerify(result.get('encryptedPassword'),
result.get('authTag'),
masterKey,
result.get('iv'))
e.sender.send(messages.GOT_PASSWORD, result.get('username'),
password, origin, action, isUnique)
})
})
// save password
webContents.savePassword()
}
}

ipcMain.on(messages.SHOW_USERNAME_LIST, (e, origin, action, boundingRect, value) => {
const passwords = AppStore.getState().get('passwords')
if (!passwords || passwords.size === 0) {
return
}
const updatePassword = (username, origin, tabId) => {
if (!origin) {
return
}

let usernames = {}
let results = passwords.filter((password) => {
return password.get('username') &&
password.get('username').startsWith(value) &&
password.get('origin') === origin &&
password.get('action') === action
var message = username
? locale.translation('notificationUpdatePasswordWithUserName').replace(/{{\s*username\s*}}/, username).replace(/{{\s*origin\s*}}/, origin)
: locale.translation('notificationUpdatePassword').replace(/{{\s*origin\s*}}/, origin)

if (!(message in passwordCallbacks)) {
// Notification not shown already
appActions.showNotification({
buttons: [
{text: locale.translation('yes')},
{text: locale.translation('no')}
],
options: {
persist: false,
advancedText: locale.translation('notificationPasswordSettings'),
advancedLink: 'about:passwords'
},
message
})
}

if (results.size === 0) {
if (BrowserWindow.getFocusedWindow()) {
BrowserWindow.getFocusedWindow().webContents.send(messages.HIDE_CONTEXT_MENU)
}
return
}

masterKey = masterKey || getMasterKey()
if (!masterKey) {
console.log('Could not access master password; aborting')
return
}
const webContents = getWebContents(tabId)

results.forEach((result) => {
usernames[result.get('username')] = CryptoUtil.decryptVerify(result.get('encryptedPassword'),
result.get('authTag'),
masterKey,
result.get('iv')) || ''
})
let win = BrowserWindow.getFocusedWindow()
if (!win) {
return
}
if (Object.keys(usernames).length > 0) {
win.webContents.send(messages.SHOW_USERNAME_LIST,
usernames, origin, action,
boundingRect)
} else {
win.webContents.send(messages.HIDE_CONTEXT_MENU)
}
})
passwordCallbacks[message] = (buttonIndex) => {
delete passwordCallbacks[message]
appActions.hideNotification(message)

ipcMain.on(messages.SAVE_PASSWORD, (e, username, password, origin, action) => {
if (!password || !origin || !action) {
return
}
const originSettings = siteSettings.getSiteSettingsForURL(AppStore.getState().get('siteSettings'), origin)
if (originSettings && originSettings.get('savePasswords') === false) {
if (buttonIndex === 0) {
webContents.updatePassword()
return
}
// never save
webContents.noUpdatePassword()
}
}

const migrate = (state) => {
const passwords = state.get('passwords')
if (passwords.size) {
masterKey = masterKey || getMasterKey()
if (!masterKey) {
console.log('Could not access master password; aborting')
return
}

const passwords = AppStore.getState().get('passwords')

// If the same password already exists, don't offer to save it
let result = passwords.findLast((pw) => {
return pw.get('origin') === origin && pw.get('action') === action && (username ? pw.get('username') === username : !pw.get('username'))
})
if (result && password === CryptoUtil.decryptVerify(result.get('encryptedPassword'),
result.get('authTag'),
masterKey,
result.get('iv'))) {
return
}

var message = username
? locale.translation('notificationPasswordWithUserName').replace(/{{\s*username\s*}}/, username).replace(/{{\s*origin\s*}}/, origin)
: locale.translation('notificationPassword').replace(/{{\s*origin\s*}}/, origin)

if (!(message in passwordCallbacks)) {
// Notification not shown already
appActions.showNotification({
buttons: [
{text: locale.translation('yes')},
{text: locale.translation('no')},
{text: locale.translation('neverForThisSite')}
],
options: {
persist: false,
advancedText: locale.translation('notificationPasswordSettings'),
advancedLink: 'about:passwords'
},
message
})
}

passwordCallbacks[message] = (buttonIndex) => {
delete passwordCallbacks[message]
appActions.hideNotification(message)

if (buttonIndex === 1) {
return
}
if (buttonIndex === 2) {
// Never save the password on this site
appActions.changeSiteSetting(origin, 'savePasswords', false)
return
passwords.forEach((password) => {
let decrypted = CryptoUtil.decryptVerify(password.get('encryptedPassword'),
password.get('authTag'),
masterKey,
password.get('iv'))
if (decrypted) {
let form = {}
form['origin'] = password.get('origin')
form['signon_realm'] = password.get('origin') + '/'
form['action'] = password.get('action')
form['username'] = password.get('username')
form['password'] = decrypted
autofill.addLogin(form)
}

// Save the password
const encrypted = CryptoUtil.encryptAuthenticate(password, masterKey)
appActions.savePassword({
origin,
action,
username: username || '',
encryptedPassword: encrypted.content,
authTag: encrypted.tag,
iv: encrypted.iv
})
}
})
})
state = state.set('legacyPasswords', state.get('passwords'))
state = state.set('passwords', new Immutable.List())
}
const allSiteSettings = state.get('siteSettings')
const blackedList = allSiteSettings.filter((setting) => setting.get('savePasswords') === false)
if (blackedList.size) {
blackedList.forEach((entry, index) => {
let form = {}
form['origin'] = index
form['signon_realm'] = index + '/'
form['blacklisted_by_user'] = true
autofill.addLogin(form)
appActions.deletePasswordSite(index)
})
}
return state
}

const passwordManagerReducer = (state, action, immutableAction) => {
action = immutableAction || makeImmutable(action)
switch (action.get('actionType')) {
case appConstants.APP_SET_STATE:
init()
state = migrate(state)
break
case appConstants.APP_SAVE_PASSWORD:
savePassword(action.get('username'), action.get('origin'), action.get('tabId'))
break
case appConstants.APP_UPDATE_PASSWORD:
updatePassword(action.get('username'), action.get('origin'), action.get('tabId'))
break
}
return state
Expand Down
19 changes: 16 additions & 3 deletions app/browser/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,13 @@ const updateAboutDetails = (tab, tabValue) => {
downloads: downloads.toJS()
})
} else if (location === 'about:passwords' && passwords) {
tab.send(messages.PASSWORD_DETAILS_UPDATED, passwords.toJS())
tab.send(messages.PASSWORD_SITE_DETAILS_UPDATED,
allSiteSettings.filter((setting) => setting.get('savePasswords') === false).toJS())
const defaultSession = session.defaultSession
defaultSession.autofill.getAutofillableLogins((result) => {
tab.send(messages.PASSWORD_DETAILS_UPDATED, result)
})
defaultSession.autofill.getBlackedlistLogins((result) => {
tab.send(messages.PASSWORD_SITE_DETAILS_UPDATED, result)
})
} else if (location === 'about:flash') {
tab.send(messages.BRAVERY_DEFAULTS_UPDATED, braveryDefaults.toJS())
} else if (location === 'about:newtab') {
Expand Down Expand Up @@ -353,7 +357,16 @@ const api = {
console.log('responsive')
})

tab.on('save-password', (e, username, origin) => {
appActions.savePassword(username, origin, tabId)
})

tab.on('update-password', (e, username, origin) => {
appActions.updatePassword(username, origin, tabId)
})

updateWebContents(tabId, tab)

let tabValue = getTabValue(tabId)
if (tabValue) {
appActions.tabCreated(tabValue)
Expand Down
1 change: 0 additions & 1 deletion app/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ let generateBraveManifest = () => {
],
js: [
'content/scripts/adInsertion.js',
'content/scripts/passwordManager.js',
'content/scripts/pageInformation.js',
'content/scripts/flashListener.js'
]
Expand Down
Loading

0 comments on commit 2e67bc3

Please sign in to comment.