Skip to content

Commit 2ff1fc7

Browse files
aenariosebn
authored andcommitted
gui: Use electron-proxy-agent & electron-fetch (#878)
(to support corporate proxies)
1 parent 9f469b4 commit 2ff1fc7

File tree

5 files changed

+306
-36
lines changed

5 files changed

+306
-36
lines changed

gui/main.js

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const notify = require('electron-main-notification')
88
const debounce = require('lodash.debounce')
99
const path = require('path')
1010

11+
const setupProxy = require('./src/main/proxy')
12+
1113
const autoLaunch = require('./src/main/autolaunch')
1214
const lastFiles = require('./src/main/lastfiles')
1315
const tray = require('./src/main/tray')
@@ -221,35 +223,38 @@ if (shouldExit) {
221223
}
222224

223225
app.on('ready', () => {
224-
log.info('Loading CLI...')
225-
desktop = new Desktop(process.env.COZY_DESKTOP_DIR)
226-
i18n.init(app)
227-
tray.init(app, toggleWindow)
228-
lastFiles.init(desktop)
229-
log.trace('Setting up tray WM...')
230-
trayWindow = new TrayWM(app, desktop)
231-
log.trace('Setting up help WM...')
232-
helpWindow = new HelpWM(app, desktop)
233-
log.trace('Setting up onboarding WM...')
234-
onboardingWindow = new OnboardingWM(app, desktop)
235-
onboardingWindow.onOnboardingDone(() => {
236-
onboardingWindow.hide()
237-
trayWindow.show().then(() => startSync())
238-
})
239-
log.trace('Setting up updater WM...')
240-
updaterWindow = new UpdaterWM(app, desktop)
241-
updaterWindow.onUpToDate(() => {
242-
updaterWindow.hide()
243-
showWindowStartApp()
244-
})
245-
updaterWindow.checkForUpdates()
226+
const {session} = require('electron')
227+
setupProxy(app, session, () => {
228+
log.info('Loading CLI...')
229+
desktop = new Desktop(process.env.COZY_DESKTOP_DIR)
230+
i18n.init(app)
231+
tray.init(app, toggleWindow)
232+
lastFiles.init(desktop)
233+
log.trace('Setting up tray WM...')
234+
trayWindow = new TrayWM(app, desktop)
235+
log.trace('Setting up help WM...')
236+
helpWindow = new HelpWM(app, desktop)
237+
log.trace('Setting up onboarding WM...')
238+
onboardingWindow = new OnboardingWM(app, desktop)
239+
onboardingWindow.onOnboardingDone(() => {
240+
onboardingWindow.hide()
241+
trayWindow.show().then(() => startSync())
242+
})
243+
log.trace('Setting up updater WM...')
244+
updaterWindow = new UpdaterWM(app, desktop)
245+
updaterWindow.onUpToDate(() => {
246+
updaterWindow.hide()
247+
showWindowStartApp()
248+
})
249+
updaterWindow.checkForUpdates()
246250

247-
// Os X wants all application to have a menu
248-
Menu.setApplicationMenu(buildAppMenu(app))
251+
// Os X wants all application to have a menu
252+
Menu.setApplicationMenu(buildAppMenu(app))
249253

250-
// On OS X it's common to re-create a window in the app when the
251-
// dock icon is clicked and there are no other windows open.
252-
app.on('activate', showWindow)
254+
// On OS X it's common to re-create a window in the app when the
255+
// dock icon is clicked and there are no other windows open.
256+
app.on('activate', showWindow)
257+
})
253258
})
254259

255260
// Don't quit the app when all windows are closed, keep the tray icon

gui/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,15 @@
6161
"auto-launch-patched": "5.0.2",
6262
"babel-polyfill": "^6.23.0",
6363
"cozy-desktop": "file:../cli/",
64+
"electron-fetch": "^1.1.0",
6465
"electron-main-notification": "1.0.1",
6566
"electron-positioner": "^3.0.0",
67+
"electron-proxy-agent": "^1.0.2",
6668
"electron-updater": "1.15.0",
6769
"lnk": "^1.1.0",
6870
"lodash.debounce": "4.0.8",
69-
"node-uuid": "^1.4.8"
71+
"node-uuid": "^1.4.8",
72+
"yargs": "^10.0.3"
7073
},
7174
"devDependencies": {
7275
"chokidar-cli": "^1.2.0",

gui/src/main/onboarding.window.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ module.exports = class OnboardingWM extends WindowManager {
7575
},
7676
(err) => {
7777
log.error(err)
78-
event.sender.send('registration-error', translate('Address No cozy instance at this address!'))
78+
if (err.code.match(/PROXY/)) {
79+
session.defaultSession.resolveProxy(cozyUrl, (p) => {
80+
event.sender.send('registration-error', translate('Address Proxy issue') + p)
81+
})
82+
} else {
83+
event.sender.send('registration-error', translate('Address No cozy instance at this address!'))
84+
}
7985
}
8086
)
8187
}

gui/src/main/proxy.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* eslint standard/no-callback-literal: 0 */
2+
3+
const ElectronProxyAgent = require('electron-proxy-agent')
4+
const url = require('url')
5+
const http = require('http')
6+
const https = require('https')
7+
8+
const log = require('cozy-desktop').default.logger({
9+
component: 'GUI:proxy'
10+
})
11+
12+
const config = require('yargs')
13+
.env('COZY_DRIVE')
14+
.conflicts('proxy-script', 'proxy-rules')
15+
.describe('proxy-script', 'The URL associated with the PAC file.')
16+
.describe('proxy-rules', 'Rules indicating which proxies to use.')
17+
.describe('proxy-bypassrules', 'Rules indicating which URLs should bypass the proxy settings. ' +
18+
'See https://github.com/electron/electron/blob/master/docs/api/session.md#sessetproxyconfig-callback')
19+
.default('proxy-ntlm-domains', '*')
20+
.describe('proxy-ntlm-domains', 'A comma-separated list of servers for which integrated authentication is enabled. ' +
21+
'Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate authentication.')
22+
.describe('login-by-realm', 'comma-separated list of realm:user:password')
23+
.help('help')
24+
.parse()
25+
26+
log.debug({config}, 'argv')
27+
28+
const formatCertificate = (certif) => `Certificate(${certif.issuerName} ${certif.subjectName})`
29+
30+
module.exports = (app, session, doneSetup) => {
31+
const loginByRealm = {}
32+
if (config['login-by-realm']) {
33+
config['login-by-realm'].split(',').forEach((lbr) => {
34+
const [realm, username, ...password] = lbr.split(':')
35+
loginByRealm[realm] = [username, password.join(':')]
36+
})
37+
}
38+
39+
if (config['proxy-ntlm-domains']) {
40+
session.defaultSession.allowNTLMCredentialsForDomains(config['proxy-ntlm-domains'])
41+
}
42+
43+
session.defaultSession.setCertificateVerifyProc((request, callback) => {
44+
const {hostname, certificate, verificationResult, errorCode} = request
45+
if (verificationResult < 0) {
46+
log.warn({hostname, certificate: formatCertificate(certificate), verificationResult, errorCode}, 'Certificate Verification Error')
47+
} else {
48+
log.debug({hostname, certificate: formatCertificate(certificate), verificationResult, errorCode}, 'Certificate Validated')
49+
}
50+
callback(-3) // use chrome validation
51+
})
52+
53+
app.on('select-client-certificate', (event, webContents, url, list, callback) => {
54+
log.debug({url}, 'select-client-certificate')
55+
callback()
56+
})
57+
58+
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
59+
log.warn({url, error, certificate: formatCertificate(certificate)}, 'App Certificate Error')
60+
callback(false)
61+
})
62+
63+
app.on('login', (event, webContents, request, authInfo, callback) => {
64+
log.debug({request: request.method + ' ' + request.url, authInfo}, 'Login event')
65+
const auth = loginByRealm[authInfo.realm]
66+
if (auth) {
67+
event.preventDefault()
68+
callback(...auth)
69+
} else {
70+
callback()
71+
}
72+
})
73+
74+
const electronFetch = require('electron-fetch')
75+
global.fetch = (url, opts = {}) => {
76+
opts.session = session.defaultSession
77+
return electronFetch(url, opts)
78+
}
79+
http.Agent.globalAgent = http.globalAgent = https.globalAgent = new ElectronProxyAgent(session.defaultSession)
80+
const _httpRequest = http.request
81+
http.request = function (options, cb) {
82+
log.warn(options, 'USING RAW HTTP REQUEST')
83+
options.agent = options.agent || http.globalAgent
84+
options.headers = options.headers || {}
85+
options.headers.host = options.hostname
86+
return _httpRequest.call(http, options, cb)
87+
}
88+
const _httpsRequest = https.request
89+
https.request = function (options, cb) {
90+
log.warn(options, 'USING RAW HTTPS REQUEST')
91+
if (typeof options === 'string') {
92+
options = url.parse(options)
93+
} else {
94+
options = Object.assign({}, options)
95+
}
96+
options.agent = options.agent || https.globalAgent
97+
return _httpsRequest.call(https, options, cb)
98+
}
99+
100+
if (config['proxy-script'] || config['proxy-rules']) {
101+
session.defaultSession.setProxy({
102+
pacScript: config['proxy-script'],
103+
proxyRules: config['proxy-rules'],
104+
proxyBypassRules: config['proxy-bypassrules']
105+
}, doneSetup)
106+
} else doneSetup()
107+
}

0 commit comments

Comments
 (0)