Skip to content

Commit 2790375

Browse files
feat(bitwarden): added field for server url during setup for self-hosted servers
fix(bitwarden): fixed confusing cache logic that does not cache credential request responses properly refactor(prompt): retrofitted the `createPrompt` function with promise refactor(prompt): updated `createPrompt` usage for the promise retrofit refactor: unwrapped AutofillSetup as individual functions and exported the `initialize` function that is used externally refactor: unwrapped PasswordManagers as individual functions and exported functions that are used externally refactor: replaced some usages of `var` with `let` and `const`
1 parent 38ca792 commit 2790375

File tree

6 files changed

+276
-274
lines changed

6 files changed

+276
-274
lines changed

js/autofillSetup.js

+22-21
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,30 @@ const setupDialog = require('passwordManager/managerSetup.js')
22
const settings = require('util/settings/settings.js')
33
const PasswordManagers = require('passwordManager/passwordManager.js')
44

5-
const AutofillSetup = {
6-
checkSettings: function () {
7-
const manager = PasswordManagers.getActivePasswordManager()
5+
async function checkSettings () {
6+
const manager = PasswordManagers.getActivePasswordManager()
7+
8+
if (!manager) {
9+
return
10+
}
11+
12+
try {
13+
const configured = await manager.checkIfConfigured()
14+
if (!configured) {
15+
setupDialog.show(manager)
16+
}
17+
} catch (e) {
18+
console.error(e)
19+
}
20+
}
21+
22+
function initialize () {
23+
settings.listen('passwordManager', manager => {
824
if (!manager) {
925
return
1026
}
11-
12-
manager.checkIfConfigured().then((configured) => {
13-
if (!configured) {
14-
setupDialog.show(manager)
15-
}
16-
}).catch((err) => {
17-
console.error(err)
18-
})
19-
},
20-
initialize: function () {
21-
settings.listen('passwordManager', function (manager) {
22-
if (manager) {
23-
// Trigger the check on browser launch and after manager is enabled
24-
AutofillSetup.checkSettings()
25-
}
26-
})
27-
}
27+
checkSettings()
28+
})
2829
}
2930

30-
module.exports = AutofillSetup
31+
module.exports = { initialize }

js/passwordManager/bitwarden.js

+64-63
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
const ProcessSpawner = require('util/process.js')
2-
const path = require('path')
1+
2+
const { join } = require('path')
33
const fs = require('fs')
4-
var { ipcRenderer } = require('electron')
4+
const { ipcRenderer } = require('electron')
5+
6+
const ProcessSpawner = require('util/process.js')
57

68
// Bitwarden password manager. Requires session key to unlock the vault.
79
class Bitwarden {
@@ -12,18 +14,17 @@ class Bitwarden {
1214
}
1315

1416
getDownloadLink () {
15-
switch (window.platformType) {
16-
case 'mac':
17-
return 'https://vault.bitwarden.com/download/?app=cli&platform=macos'
18-
case 'windows':
19-
return 'https://vault.bitwarden.com/download/?app=cli&platform=windows'
20-
case 'linux':
21-
return 'https://vault.bitwarden.com/download/?app=cli&platform=linux'
17+
if (window.platformType === 'mac') {
18+
return 'https://vault.bitwarden.com/download/?app=cli&platform=macos'
2219
}
20+
if (window.platformType === 'windows') {
21+
return 'https://vault.bitwarden.com/download/?app=cli&platform=windows'
22+
}
23+
return 'https://vault.bitwarden.com/download/?app=cli&platform=linux'
2324
}
2425

2526
getLocalPath () {
26-
return path.join(window.globalArgs['user-data-path'], 'tools', (platformType === 'windows' ? 'bw.exe' : 'bw'))
27+
return join(window.globalArgs['user-data-path'], 'tools', (platformType === 'windows' ? 'bw.exe' : 'bw'))
2728
}
2829

2930
getSetupMode () {
@@ -41,7 +42,7 @@ class Bitwarden {
4142
try {
4243
await fs.promises.access(localPath, fs.constants.X_OK)
4344
local = true
44-
} catch (e) { }
45+
} catch { }
4546
if (local) {
4647
return localPath
4748
}
@@ -71,7 +72,7 @@ class Bitwarden {
7172

7273
// Tries to get a list of credential suggestions for a given domain name.
7374
async getSuggestions (domain) {
74-
if (this.lastCallList[domain] != null) {
75+
if (this.lastCallList[domain]) {
7576
return this.lastCallList[domain]
7677
}
7778

@@ -84,32 +85,31 @@ class Bitwarden {
8485
throw new Error()
8586
}
8687

87-
this.lastCallList[domain] = this.loadSuggestions(command, domain).then(suggestions => {
88-
this.lastCallList[domain] = null
88+
try {
89+
const suggestions = await this.loadSuggestions(command, domain)
90+
this.lastCallList[domain] = suggestions
8991
return suggestions
90-
}).catch(ex => {
92+
} catch (e) {
9193
this.lastCallList[domain] = null
92-
})
94+
}
9395

9496
return this.lastCallList[domain]
9597
}
9698

9799
// Loads credential suggestions for given domain name.
98100
async loadSuggestions (command, domain) {
99101
try {
100-
const process = new ProcessSpawner(command, ['list', 'items', '--url', this.sanitize(domain), '--session', this.sessionKey])
101-
const data = await process.execute()
102-
103-
const matches = JSON.parse(data)
104-
const credentials = matches.map(match => {
105-
const { login: { username, password } } = match
106-
return { username, password, manager: 'Bitwarden' }
107-
})
108-
109-
return credentials
110-
} catch (ex) {
111-
const { error, data } = ex
112-
console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error)
102+
const process = new ProcessSpawner(
103+
command,
104+
['list', 'items', '--url', domain.replace(/[^a-zA-Z0-9.-]/g, ''), '--session', this.sessionKey]
105+
)
106+
const matches = JSON.parse(await process.execute())
107+
return matches.map(
108+
({ login: { username, password } }) =>
109+
({ username, password, manager: 'Bitwarden' })
110+
)
111+
} catch ({ error, data }) {
112+
console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`)
113113
return []
114114
}
115115
}
@@ -118,9 +118,8 @@ class Bitwarden {
118118
try {
119119
const process = new ProcessSpawner(command, ['sync', '--session', this.sessionKey])
120120
await process.execute()
121-
} catch (ex) {
122-
const { error, data } = ex
123-
console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error)
121+
} catch ({ error, data }) {
122+
console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`)
124123
}
125124
}
126125

@@ -138,17 +137,17 @@ class Bitwarden {
138137
await this.forceSync(this.path)
139138

140139
return true
141-
} catch (ex) {
142-
const { error, data } = ex
140+
} catch (err) {
141+
const { error, data } = err
143142

144-
console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error)
143+
console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`)
145144

146145
if (error.includes('not logged in')) {
147146
await this.signInAndSave()
148147
return await this.unlockStore(password)
149148
}
150149

151-
throw ex
150+
throw err
152151
}
153152
}
154153

@@ -161,41 +160,43 @@ class Bitwarden {
161160
console.warn(e)
162161
}
163162

164-
// show credentials dialog
165-
166-
var signInFields = [
163+
// show ask-for-credential dialog
164+
const signInFields = [
165+
{ placeholder: 'Server URL (Leave blank for the default Bitwarden server)', id: 'url', type: 'text' },
167166
{ placeholder: 'Client ID', id: 'clientID', type: 'password' },
168167
{ placeholder: 'Client Secret', id: 'clientSecret', type: 'password' }
169168
]
170169

171-
const credentials = ipcRenderer.sendSync('prompt', {
172-
text: l('passwordManagerBitwardenSignIn'),
173-
values: signInFields,
174-
ok: l('dialogConfirmButton'),
175-
cancel: l('dialogSkipButton'),
176-
width: 500,
177-
height: 260
178-
})
179-
180-
for (const key in credentials) {
181-
if (credentials[key] === '') {
182-
throw new Error('no credentials entered')
170+
const credentials = ipcRenderer.sendSync(
171+
'prompt',
172+
{
173+
text: l('passwordManagerBitwardenSignIn'),
174+
values: signInFields,
175+
ok: l('dialogConfirmButton'),
176+
cancel: l('dialogSkipButton'),
177+
width: 500,
178+
height: 260
183179
}
184-
}
180+
)
185181

186-
const process = new ProcessSpawner(path, ['login', '--apikey'], {
187-
BW_CLIENTID: credentials.clientID.trim(),
188-
BW_CLIENTSECRET: credentials.clientSecret.trim()
189-
})
182+
if (credentials.clientID === '' || credentials.clientSecret === '') {
183+
throw new Error('no credentials entered')
184+
}
190185

191-
await process.execute()
186+
credentials.url = credentials.url || 'bitwarden.com'
192187

193-
return true
194-
}
188+
const process1 = new ProcessSpawner(path, ['config', 'server', credentials.url.trim()])
189+
await process1.execute()
195190

196-
// Basic domain name cleanup. Removes any non-ASCII symbols.
197-
sanitize (domain) {
198-
return domain.replace(/[^a-zA-Z0-9.-]/g, '')
191+
const process2 = new ProcessSpawner(
192+
path,
193+
['login', '--apikey'],
194+
{
195+
BW_CLIENTID: credentials.clientID.trim(),
196+
BW_CLIENTSECRET: credentials.clientSecret.trim()
197+
}
198+
)
199+
await process2.execute()
199200
}
200201
}
201202

js/passwordManager/passwordCapture.js

+29-26
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ const passwordCapture = {
1313
closeButton: document.getElementById('password-capture-ignore'),
1414
currentDomain: null,
1515
barHeight: 0,
16-
showCaptureBar: function (username, password) {
16+
17+
showCaptureBar (username, password) {
1718
passwordCapture.description.textContent = l('passwordCaptureSavePassword').replace('%s', passwordCapture.currentDomain)
1819
passwordCapture.bar.hidden = false
1920

@@ -27,15 +28,17 @@ const passwordCapture = {
2728
passwordCapture.barHeight = passwordCapture.bar.getBoundingClientRect().height
2829
webviews.adjustMargin([passwordCapture.barHeight, 0, 0, 0])
2930
},
30-
hideCaptureBar: function () {
31+
32+
hideCaptureBar () {
3133
webviews.adjustMargin([passwordCapture.barHeight * -1, 0, 0, 0])
3234

3335
passwordCapture.bar.hidden = true
3436
passwordCapture.usernameInput.value = ''
3537
passwordCapture.passwordInput.value = ''
3638
passwordCapture.currentDomain = null
3739
},
38-
togglePasswordVisibility: function () {
40+
41+
togglePasswordVisibility () {
3942
if (passwordCapture.passwordInput.type === 'password') {
4043
passwordCapture.passwordInput.type = 'text'
4144
passwordCapture.revealButton.classList.remove('carbon:view')
@@ -46,8 +49,9 @@ const passwordCapture = {
4649
passwordCapture.revealButton.classList.remove('carbon:view-off')
4750
}
4851
},
49-
handleRecieveCredentials: function (tab, args, frameId) {
50-
var domain = args[0][0]
52+
53+
async handleRecieveCredentials (tab, args, frameId) {
54+
let domain = args[0][0]
5155
if (domain.startsWith('www.')) {
5256
domain = domain.slice(4)
5357
}
@@ -56,30 +60,29 @@ const passwordCapture = {
5660
return
5761
}
5862

59-
var username = args[0][1] || ''
60-
var password = args[0][2] || ''
63+
const username = args[0][1] || ''
64+
const password = args[0][2] || ''
6165

62-
PasswordManagers.getConfiguredPasswordManager().then(function (manager) {
63-
if (!manager || !manager.saveCredential) {
64-
// the password can't be saved
65-
return
66+
const manager = await PasswordManagers.getConfiguredPasswordManager()
67+
if (!manager || !manager.saveCredential) {
68+
// the password can't be saved
69+
return
70+
}
71+
72+
// check if this username/password combo is already saved
73+
const credentials = await manager.getSuggestions(domain)
74+
const alreadyExists = credentials.some(cred => cred.username === username && cred.password === password)
75+
if (!alreadyExists) {
76+
if (!passwordCapture.bar.hidden) {
77+
passwordCapture.hideCaptureBar()
6678
}
6779

68-
// check if this username/password combo is already saved
69-
manager.getSuggestions(domain).then(function (credentials) {
70-
var alreadyExists = credentials.some(cred => cred.username === username && cred.password === password)
71-
if (!alreadyExists) {
72-
if (!passwordCapture.bar.hidden) {
73-
passwordCapture.hideCaptureBar()
74-
}
75-
76-
passwordCapture.currentDomain = domain
77-
passwordCapture.showCaptureBar(username, password)
78-
}
79-
})
80-
})
80+
passwordCapture.currentDomain = domain
81+
passwordCapture.showCaptureBar(username, password)
82+
}
8183
},
82-
initialize: function () {
84+
85+
initialize () {
8386
passwordCapture.usernameInput.placeholder = l('username')
8487
passwordCapture.passwordInput.placeholder = l('password')
8588

@@ -106,7 +109,7 @@ const passwordCapture = {
106109
// the bar can change height when the window is resized, so the webview needs to be resized in response
107110
window.addEventListener('resize', function () {
108111
if (!passwordCapture.bar.hidden) {
109-
var oldHeight = passwordCapture.barHeight
112+
const oldHeight = passwordCapture.barHeight
110113
passwordCapture.barHeight = passwordCapture.bar.getBoundingClientRect().height
111114
webviews.adjustMargin([passwordCapture.barHeight - oldHeight, 0, 0, 0])
112115
}

0 commit comments

Comments
 (0)