diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index d95c0e99f..cfdb14717 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -80,6 +80,10 @@ module.exports.create = function (Env, cb) { return void cb(); } + // If the channel is restricted, send the history keeper ID so that they + // can try to authenticate + allowed.unshift(Env.id); + // otherwise they're not allowed. // respond with a special error that includes the list of keys // which would be allowed... diff --git a/www/common/cryptget.js b/www/common/cryptget.js index ab707a49b..e394788d7 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -1,12 +1,15 @@ define([ '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', + '/bower_components/netflux-websocket/netflux-client.js', '/common/common-util.js', '/common/common-hash.js', '/common/common-realtime.js', '/common/outer/network-config.js', + '/common/pinpad.js', + '/bower_components/nthen/index.js', '/bower_components/chainpad/chainpad.dist.js', -], function (Crypto, CPNetflux, Util, Hash, Realtime, NetConfig) { +], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Pinpad, nThen) { var finish = function (S, err, doc) { if (S.done) { return; } S.cb(err, doc); @@ -28,6 +31,50 @@ define([ } }; + var makeNetwork = function (cb) { + var wsUrl = NetConfig.getWebsocketURL(); + Netflux.connect(wsUrl).then(function (network) { + cb(null, network); + }, function (err) { + cb(err); + }); + }; + + var start = function (Session, config) { + // Create a network and authenticate with all our keys if necessary, + // then start chainpad-netflux + nThen(function (waitFor) { + if (Session.hasNetwork) { return; } + makeNetwork(waitFor(function (err, network) { + if (err) { return; } + config.network = network; + })); + }).nThen(function () { + Session.realtime = CPNetflux.start(config); + }); + }; + + var onRejected = function (config, Session, data, cb) { + // Check if we can authenticate + if (!Array.isArray(data) || !data.length || data[0].length !== 16) { + return void cb(true); + } + if (!Array.isArray(Session.accessKeys)) { return void cb(true); } + + // Authenticate + config.network.historyKeeper = data[0]; + nThen(function (waitFor) { + Session.accessKeys.forEach(function (obj) { + Pinpad.create(config.network, obj, waitFor(function (e) { + console.log('done', obj); + if (e) { console.error(e); } + })); + }); + }).nThen(function () { + cb(); + }); + }; + var makeConfig = function (hash, opt) { var secret; if (typeof(hash) === 'string') { @@ -67,7 +114,15 @@ define([ progress = progress || function () {}; var config = makeConfig(hash, opt); - var Session = { cb: cb, hasNetwork: Boolean(opt.network) }; + var Session = { + cb: cb, + accessKeys: opt.accessKeys, + hasNetwork: Boolean(opt.network) + }; + + config.onRejected = function (data, cb) { + onRejected(config, Session, data, cb); + }; config.onReady = function (info) { var rt = Session.session = info.realtime; @@ -95,7 +150,7 @@ define([ overwrite(config, opt); - Session.realtime = CPNetflux.start(config); + start(Session, config); }; var put = function (hash, doc, cb, opt) { @@ -105,7 +160,15 @@ define([ opt = opt || {}; var config = makeConfig(hash, opt); - var Session = { cb: cb, hasNetwork: Boolean(opt.network) }; + var Session = { + cb: cb, + accessKeys: opt.accessKeys, + hasNetwork: Boolean(opt.network) + }; + + config.onRejected = function (data, cb) { + onRejected(config, Session, data, cb); + }; config.onReady = function (info) { var realtime = Session.session = info.realtime; @@ -126,7 +189,7 @@ define([ }; overwrite(config, opt); - Session.session = CPNetflux.start(config); + start(Session, config); }; return { diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index d7c580f90..4613e5756 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -68,6 +68,38 @@ define([ }, cb); }; + common.getAccessKeys = function (cb) { + var keys = []; + Nthen(function (waitFor) { + // Push account keys + postMessage("GET", { + key: ['edPrivate'], + }, waitFor(function (obj) { + if (obj.error) { return; } + try { + keys.push({ + edPrivate: obj, + edPublic: Hash.getSignPublicFromPrivate(obj) + }); + } catch (e) { console.error(e); } + })); + // Push teams keys + postMessage("GET", { + key: ['teams'], + }, waitFor(function (obj) { + if (obj.error) { return; } + Object.keys(obj || {}).forEach(function (id) { + var t = obj[id]; + var _keys = t.keys.drive || {}; + if (!_keys.edPrivate) { return; } + keys.push(t.keys.drive); + }); + })); + }).nThen(function () { + cb(keys); + }); + }; + common.makeNetwork = function (cb) { require([ '/bower_components/netflux-websocket/netflux-client.js', @@ -629,6 +661,10 @@ define([ optsPut.password = password; })); } + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + optsPut.accessKeys = keys; + })); }).nThen(function () { Crypt.get(parsed.hash, function (err, val) { if (err) { @@ -667,19 +703,28 @@ define([ password: data.password, initialState: parsed.type === 'poll' ? '{}' : undefined }; - Crypt.get(parsed.hash, _waitFor(function (err, _val) { - if (err) { - _waitFor.abort(); - return void cb(err); - } - try { - val = JSON.parse(_val); - fixPadMetadata(val, true); - } catch (e) { - _waitFor.abort(); - return void cb(e.message); - } - }), optsGet); + var next = _waitFor(); + Nthen(function (waitFor) { + // Authenticate in case the pad os restricted + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + })); + }).nThen(function () { + Crypt.get(parsed.hash, function (err, _val) { + if (err) { + _waitFor.abort(); + return void cb(err); + } + try { + val = JSON.parse(_val); + fixPadMetadata(val, true); + next(); + } catch (e) { + _waitFor.abort(); + return void cb(e.message); + } + }, optsGet); + }); return; } @@ -742,9 +787,6 @@ define([ }).nThen(function () { Crypt.put(parsed2.hash, JSON.stringify(val), function () { cb(); - Crypt.get(parsed2.hash, function (err, val) { - console.warn(val); - }); }, optsPut); }); @@ -1007,7 +1049,7 @@ define([ oldSecret = Hash.getSecrets(parsed.type, parsed.hash, optsGet.password); oldChannel = oldSecret.channel; common.getPadMetadata({channel: oldChannel}, waitFor(function (metadata) { - oldMetadata = metadata; + oldMetadata = metadata || {}; })); common.getMetadata(waitFor(function (err, data) { if (err) { @@ -1059,6 +1101,11 @@ define([ if (expire) { optsPut.metadata.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds } + }).nThen(function (waitFor) { + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + optsPut.accessKeys = keys; + })); }).nThen(function (waitFor) { Crypt.get(parsed.hash, waitFor(function (err, val) { if (err) { @@ -1075,6 +1122,8 @@ define([ } }), optsGet); }).nThen(function (waitFor) { + optsPut.metadata.restricted = oldMetadata.restricted; + optsPut.metadata.allowed = oldMetadata.allowed; Crypt.put(newHash, cryptgetVal, waitFor(function (err) { if (err) { waitFor.abort(); @@ -1310,11 +1359,17 @@ define([ validateKey: newSecret.keys.validateKey }, }; + var optsGet = {}; Nthen(function (waitFor) { common.getPadAttribute('', waitFor(function (err, _data) { padData = _data; + optsGet.password = padData.password; }), href); + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + optsPut.accessKeys = keys; + })); }).nThen(function (waitFor) { oldSecret = Hash.getSecrets(parsed.type, parsed.hash, padData.password); @@ -1393,9 +1448,7 @@ define([ waitFor.abort(); return void cb({ error: 'CANT_PARSE' }); } - }), { - password: padData.password - }); + }), optsGet); }).nThen(function (waitFor) { // Re-encrypt rtchannel oldRtChannel = Util.find(cryptgetVal, ['content', 'channel']); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 94dc8ab14..c837a9b72 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1382,8 +1382,10 @@ define([ }; var i = 0; sframeChan.on('Q_CRYPTGET', function (data, cb) { + var keys; var todo = function () { data.opts.network = cgNetwork; + data.opts.accessKeys = keys; Cryptget.get(data.hash, function (err, val) { cb({ error: err, @@ -1402,17 +1404,21 @@ define([ cgNetwork = undefined; } i++; - if (!cgNetwork) { - cgNetwork = true; - return void Cryptpad.makeNetwork(function (err, nw) { - console.log(nw); - cgNetwork = nw; - todo(); - }); - } else if (cgNetwork === true) { - return void whenCGReady(todo); - } - todo(); + + Cryptpad.getAccessKeys(function (_keys) { + keys = _keys; + if (!cgNetwork) { + cgNetwork = true; + return void Cryptpad.makeNetwork(function (err, nw) { + console.log(nw); + cgNetwork = nw; + todo(); + }); + } else if (cgNetwork === true) { + return void whenCGReady(todo); + } + todo(); + }); }); sframeChan.on('EV_CRYPTGET_DISCONNECT', function () { if (!cgNetwork) { return; }