Skip to content

Commit 6cf9e16

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 6cf9e16

File tree

6 files changed

+279
-274
lines changed

6 files changed

+279
-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

+67-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,34 @@ 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
91+
console.log('getSuggestions', suggestions)
8992
return suggestions
90-
}).catch(ex => {
93+
} catch (e) {
9194
this.lastCallList[domain] = null
92-
})
95+
}
9396

9497
return this.lastCallList[domain]
9598
}
9699

97100
// Loads credential suggestions for given domain name.
98101
async loadSuggestions (command, domain) {
102+
console.log(domain.replace(/[^a-zA-Z0-9.-]/g, ''))
99103
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)
104+
const process = new ProcessSpawner(
105+
command,
106+
['list', 'items', '--url', domain.replace(/[^a-zA-Z0-9.-]/g, ''), '--session', this.sessionKey]
107+
)
108+
const matches = JSON.parse(await process.execute())
109+
console.log(matches)
110+
return matches.map(
111+
({ login: { username, password } }) =>
112+
({ username, password, manager: 'Bitwarden' })
113+
)
114+
} catch ({ error, data }) {
115+
console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`)
113116
return []
114117
}
115118
}
@@ -118,9 +121,8 @@ class Bitwarden {
118121
try {
119122
const process = new ProcessSpawner(command, ['sync', '--session', this.sessionKey])
120123
await process.execute()
121-
} catch (ex) {
122-
const { error, data } = ex
123-
console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error)
124+
} catch ({ error, data }) {
125+
console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`)
124126
}
125127
}
126128

@@ -138,17 +140,17 @@ class Bitwarden {
138140
await this.forceSync(this.path)
139141

140142
return true
141-
} catch (ex) {
142-
const { error, data } = ex
143+
} catch (err) {
144+
const { error, data } = err
143145

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

146148
if (error.includes('not logged in')) {
147149
await this.signInAndSave()
148150
return await this.unlockStore(password)
149151
}
150152

151-
throw ex
153+
throw err
152154
}
153155
}
154156

@@ -161,41 +163,43 @@ class Bitwarden {
161163
console.warn(e)
162164
}
163165

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

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')
173+
const credentials = ipcRenderer.sendSync(
174+
'prompt',
175+
{
176+
text: l('passwordManagerBitwardenSignIn'),
177+
values: signInFields,
178+
ok: l('dialogConfirmButton'),
179+
cancel: l('dialogSkipButton'),
180+
width: 500,
181+
height: 260
183182
}
184-
}
183+
)
185184

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

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

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

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

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)