From ad9f2ff2236e04f7d337a1d4577d78766c05c7ed Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Thu, 17 Aug 2017 15:41:04 +0200 Subject: [PATCH] Made pad2 server more generic and easy to implement --- config.example.js | 14 +++++++--- server.js | 44 +++++++++++++++++++++----------- www/common/metadata-manager.js | 2 +- www/common/sframe-noscriptfix.js | 3 --- www/pad2/index.html | 3 +-- www/pad2/outer.js | 5 +++- 6 files changed, 46 insertions(+), 25 deletions(-) delete mode 100644 www/common/sframe-noscriptfix.js diff --git a/config.example.js b/config.example.js index 2602549cc..6287d57fd 100644 --- a/config.example.js +++ b/config.example.js @@ -17,7 +17,8 @@ module.exports = { httpHeaders: { "X-XSS-Protection": "1; mode=block", - "X-Content-Type-Options": "nosniff" + "X-Content-Type-Options": "nosniff", + "Access-Control-Allow-Origin": "*" }, contentSecurity: [ @@ -45,8 +46,8 @@ module.exports = { // data: is used by codemirror "img-src 'self' data: blob:", - // for accounts.cryptpad.fr authentication - "frame-ancestors 'self' accounts.cryptpad.fr", + // for accounts.cryptpad.fr authentication and pad2 cross-domain iframe sandbox + "frame-ancestors *", ].join('; '), // CKEditor requires significantly more lax content security policy in order to function. @@ -71,6 +72,13 @@ module.exports = { httpPort: 3000, + // This is for allowing the cross-domain iframe to function when developing + httpSafePort: 3001, + + // This is for deployment in production, CryptPad uses a separate origin (domain) to host the + // cross-domain iframe. It can simply host the same content as CryptPad. + // httpSafeOrigin: "https://some-other-domain.xyz", + /* your server's websocket url is configurable * (default: '/cryptpad_websocket') * diff --git a/server.js b/server.js index 1bf53f02d..faab366f6 100644 --- a/server.js +++ b/server.js @@ -38,7 +38,8 @@ var setHeaders = (function () { if (headers['Content-Security-Policy'].indexOf('frame-ancestors') === -1) { // backward compat for those who do not merge the new version of the config // when updating. This prevents endless spinner if someone clicks donate. - headers['Content-Security-Policy'] += "frame-ancestors 'self' accounts.cryptpad.fr;"; + // It also fixes the cross-domain iframe. + headers['Content-Security-Policy'] += "frame-ancestors *;"; } } const padHeaders = clone(headers); @@ -47,7 +48,7 @@ var setHeaders = (function () { } if (Object.keys(headers).length) { return function (req, res) { - const h = /^\/pad\/inner\.html.*/.test(req.url) ? padHeaders : headers; + const h = /^\/pad(2)?\/inner\.html.*/.test(req.url) ? padHeaders : headers; for (let header in h) { res.setHeader(header, h[header]); } }; } @@ -124,18 +125,29 @@ if (config.privKeyAndCertFiles) { app.get('/api/config', function(req, res){ var host = req.headers.host.replace(/\:[0-9]+/, ''); res.setHeader('Content-Type', 'text/javascript'); - res.send('define(' + JSON.stringify({ - requireConf: { - waitSeconds: 60, - urlArgs: 'ver=' + Package.version + (DEV_MODE? '-' + (+new Date()): ''), - }, - removeDonateButton: (config.removeDonateButton === true), - allowSubscriptions: (config.allowSubscriptions === true), - - websocketPath: config.useExternalWebsocket ? undefined : config.websocketPath, - websocketURL:'ws' + ((useSecureWebsockets) ? 's' : '') + '://' + host + ':' + - websocketPort + '/cryptpad_websocket', - }) + ');'); + res.send('define(function(){\n' + [ + 'var obj = ' + JSON.stringify({ + requireConf: { + waitSeconds: 60, + urlArgs: 'ver=' + Package.version + (DEV_MODE? '-' + (+new Date()): ''), + }, + removeDonateButton: (config.removeDonateButton === true), + allowSubscriptions: (config.allowSubscriptions === true), + websocketPath: config.useExternalWebsocket ? undefined : config.websocketPath, + websocketURL:'ws' + ((useSecureWebsockets) ? 's' : '') + '://' + host + ':' + + websocketPort + '/cryptpad_websocket', + }, null, '\t'), + 'obj.httpSafeOrigin = ' + (function () { + if (config.httpSafeOrigin) { return config.httpSafeOrigin; } + if (config.httpSafePort) { + return "(function () { return window.location.origin.replace(/\:[0-9]+$/, ':" + + config.httpSafePort + "'); }())"; + } + return 'window.location.origin'; + }()), + 'return obj', + '});' + ].join(';\n')); }); var httpServer = httpsOpts ? Https.createServer(httpsOpts, app) : Http.createServer(app); @@ -149,7 +161,9 @@ httpServer.listen(config.httpPort,config.httpAddress,function(){ console.log('\n[%s] server available http://%s%s', new Date().toISOString(), hostName, ps); }); -Http.createServer(app).listen(config.httpPort+1, config.httpAddress); +if (config.httpSafePort) { + Http.createServer(app).listen(config.httpSafePort, config.httpAddress); +} var wsConfig = { server: httpServer }; diff --git a/www/common/metadata-manager.js b/www/common/metadata-manager.js index 930a7c138..a9c9f0b54 100644 --- a/www/common/metadata-manager.js +++ b/www/common/metadata-manager.js @@ -72,7 +72,7 @@ define([], function () { }, getMetadata: function () { checkUpdate(); - return metadataObj; + return Object.freeze(JSON.parse(JSON.stringify(metadataObj))); }, onChange: function (f) { changeHandlers.push(f); } }); diff --git a/www/common/sframe-noscriptfix.js b/www/common/sframe-noscriptfix.js deleted file mode 100644 index f5e6fb1c0..000000000 --- a/www/common/sframe-noscriptfix.js +++ /dev/null @@ -1,3 +0,0 @@ -// Fix for noscript bugs when caching iframe content. -// Caution, this file will get cached, you must change the name if you change it. -document.getElementById('sbox-iframe').setAttribute('src', 'http://localhost:3001/pad2/inner.html?cb=' + (+new Date())); diff --git a/www/pad2/index.html b/www/pad2/index.html index 0a2a3b922..4e21fbe18 100644 --- a/www/pad2/index.html +++ b/www/pad2/index.html @@ -27,5 +27,4 @@ - - + diff --git a/www/pad2/outer.js b/www/pad2/outer.js index 526035d3d..1dfdbcd5a 100644 --- a/www/pad2/outer.js +++ b/www/pad2/outer.js @@ -1,17 +1,20 @@ define([ + '/api/config', '/common/sframe-channel.js', 'jquery', '/common/sframe-chainpad-netflux-outer.js', '/bower_components/nthen/index.js', '/common/cryptpad-common.js', '/bower_components/chainpad-crypto/crypto.js' -], function (SFrameChannel, $, CpNfOuter, nThen, Cryptpad, Crypto) { +], function (ApiConfig, SFrameChannel, $, CpNfOuter, nThen, Cryptpad, Crypto) { console.log('xxx'); var sframeChan; nThen(function (waitFor) { $(waitFor()); }).nThen(function (waitFor) { + $('#sbox-iframe').attr('src', + ApiConfig.httpSafeOrigin + '/pad2/inner.html?' + ApiConfig.requireConf.urlArgs); SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) { sframeChan = sfc; console.log('sframe initialized');