diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index f17a345eb..579d1197a 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -139,6 +139,11 @@ @colortheme_notifications-color: #FFF; @colortheme_notifications-warn: #ffae00; +@colortheme_support-bg: #42d1f4; +@colortheme_support-color: #000; +@colortheme_support-warn: #9A37F7; + + // Sidebar layout (profile / settings) @colortheme_sidebar-active: #fff; @colortheme_sidebar-left-bg: #eee; diff --git a/scripts/generateAdminKeys.js b/scripts/generateAdminKeys.js new file mode 100644 index 000000000..072dd8b3d --- /dev/null +++ b/scripts/generateAdminKeys.js @@ -0,0 +1,26 @@ +const Nacl = require('tweetnacl'); +const Crypto = require('crypto'); + +const keyPair = Nacl.box.keyPair(); +console.log(keyPair); + +console.log("You've just generated a new key pair for your support mailbox."); + +console.log("The public key should first be added to your config.js file ('supportMailboxPublicKey'), then save and restart the server.") +console.log("Once restarted, administrators (specified with 'adminKeys' in config.js too) will be able to add the private key into their account. This can be done using the administration panel."); +console.log("You will have to send the private key to each administrator manually so that they can add it to their account."); +console.log(); +console.log("WARNING: the public and private keys must come from the same key pair to have a working encrypted support mailbox."); +console.log(); +console.log("NOTE: You can change the key pair at any time if you want to revoke access to the support mailbox. You just have to generate a new key pair using this file, and replace the value in config.js, and then send the new private key to the administrators of your choice."); + + +console.log(); +console.log(); +console.log("Your public key (add it to config.js):"); +console.log(Nacl.util.encodeBase64(keyPair.publicKey)); + +console.log(); +console.log(); +console.log("Your private key (store it in a safe place and send it to your instance's admins):"); +console.log(Nacl.util.encodeBase64(keyPair.secretKey)); diff --git a/server.js b/server.js index b10b32da8..b8c2b8163 100644 --- a/server.js +++ b/server.js @@ -193,6 +193,7 @@ app.get('/api/config', function(req, res){ httpUnsafeOrigin: config.httpUnsafeOrigin, adminEmail: config.adminEmail, adminKeys: admins, + supportMailbox: config.supportMailboxPublicKey }, null, '\t'), 'obj.httpSafeOrigin = ' + (function () { if (config.httpSafeOrigin) { return '"' + config.httpSafeOrigin + '"'; } diff --git a/www/common/common-constants.js b/www/common/common-constants.js index 636adbc01..986e115e2 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -17,6 +17,6 @@ define(function () { // Sub plan: 'CryptPad_plan', // Apps - criticalApps: ['profile', 'settings', 'debug', 'admin', 'notifications'] + criticalApps: ['profile', 'settings', 'debug', 'admin', 'support', 'notifications'] }; }); diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 3ef802681..1b588db60 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -85,6 +85,11 @@ define([ return id; }; + Hash.getChannelIdFromKey = function (publicKey) { + if (!publicKey) { return; } + return uint8ArrayToHex(Hash.decodeBase64(publicKey).subarray(0,16)); + }; + Hash.createRandomHash = function (type, password) { var cryptor; if (type === 'file') { diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 492446781..9c0dfb864 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -478,6 +478,7 @@ define([ settings: store.proxy.settings, thumbnails: disableThumbnails === false, isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners'])), + support: Util.find(store.proxy, ['mailboxes', 'support', 'channel']), pendingFriends: store.proxy.friends_pending || {} } }; diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index 550573411..cb4a4a5fe 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -10,6 +10,7 @@ define([ var TYPES = [ 'notifications', + 'support' ]; var BLOCKING_TYPES = [ ]; @@ -25,6 +26,16 @@ define([ if (res.error) { console.error(res); } }); } + if (!mailboxes['support']) { + mailboxes.support = { + channel: Hash.createChannelId(), + lastKnownHash: '', + viewed: [] + }; + ctx.pinPads([mailboxes.support.channel], function (res) { + if (res.error) { console.error(res); } + }); + } }; /* @@ -76,15 +87,30 @@ proxy.mailboxes = { var crypto = Crypto.Mailbox.createEncryptor(keys); var network = ctx.store.network; - var ciphertext = crypto.encrypt(JSON.stringify({ + var text = JSON.stringify({ type: type, content: msg - }), user.curvePublic); + }); + var ciphertext = crypto.encrypt(text, user.curvePublic); network.join(user.channel).then(function (wc) { wc.bcast(ciphertext).then(function () { cb(); wc.leave(); + + // If we've just sent a message to one of our mailboxes, we have to trigger the handler manually + // (the server won't send back our message to us) + var box; + if (Object.keys(ctx.boxes).some(function (t) { + var _box = ctx.boxes[t]; + if (_box.channel === user.channel) { + box = _box; + return true; + } + })) { + var hash = ciphertext.slice(0, 64); + box.onMessage(text, null, null, null, hash, user.curvePublic); + } }); }, function (err) { cb({error: err}); @@ -157,6 +183,7 @@ proxy.mailboxes = { var openChannel = function (ctx, type, m, onReady) { var box = ctx.boxes[type] = { + channel: m.channel, queue: [], // Store the messages to send when the channel is ready history: [], // All the hashes loaded from the server in corretc order content: {}, // Content of the messages that should be displayed @@ -213,7 +240,7 @@ proxy.mailboxes = { }); box.queue = []; }; - cfg.onMessage = function (msg, user, vKey, isCp, hash, author) { + box.onMessage = cfg.onMessage = function (msg, user, vKey, isCp, hash, author) { if (hash === m.lastKnownHash) { return; } try { msg = JSON.parse(msg); diff --git a/www/common/sframe-common-mailbox.js b/www/common/sframe-common-mailbox.js index 814966a22..4bcee18af 100644 --- a/www/common/sframe-common-mailbox.js +++ b/www/common/sframe-common-mailbox.js @@ -90,8 +90,11 @@ define([ var pushMessage = function (data, handler) { var todo = function (f) { try { - var el = createElement(data); - Notifications.add(Common, data, el); + var el; + if (data.type === 'notifications') { + el = createElement(data); + Notifications.add(Common, data, el); + } f(data, el); } catch (e) { console.error(e); @@ -108,7 +111,9 @@ define([ onViewedHandlers.forEach(function (f) { try { f(data); - Notifications.remove(Common, data); + if (data.type === 'notifications') { + Notifications.remove(Common, data); + } } catch (e) { console.error(e); } @@ -139,18 +144,25 @@ define([ var subscribed = false; // Get all existing notifications + the new ones when they come - mailbox.subscribe = function (cfg) { + mailbox.subscribe = function (types, cfg) { if (!subscribed) { execCommand('SUBSCRIBE', null, function () {}); subscribed = true; } if (typeof(cfg.onViewed) === "function") { - onViewedHandlers.push(cfg.onViewed); + onViewedHandlers.push(function (data) { + if (types.indexOf(data.type) === -1) { return; } + cfg.onViewed(data); + }); } if (typeof(cfg.onMessage) === "function") { - onMessageHandlers.push(cfg.onMessage); + onMessageHandlers.push(function (data, el) { + if (types.indexOf(data.type) === -1) { return; } + cfg.onMessage(data, el); + }); } Object.keys(history).forEach(function (type) { + if (types.indexOf(type) === -1) { return; } history[type].forEach(function (data) { pushMessage({ type: type, diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index 303a983e0..589dec496 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -983,7 +983,7 @@ MessengerUI, Messages) { $button.addClass('fa-bell'); }; - Common.mailbox.subscribe({ + Common.mailbox.subscribe(['notifications'], { onMessage: function (data, el) { if (el) { div.appendChild(el); diff --git a/www/support/app-support.less b/www/support/app-support.less new file mode 100644 index 000000000..60091a0d9 --- /dev/null +++ b/www/support/app-support.less @@ -0,0 +1,19 @@ +@import (reference) '../../customize/src/less2/include/framework.less'; +@import (reference) '../../customize/src/less2/include/sidebar-layout.less'; + +&.cp-app-support { + .framework_min_main( + @bg-color: @colortheme_support-bg, + @warn-color: @colortheme_support-warn, + @color: @colortheme_support-color + ); + .sidebar-layout_main(); + + .cp-hidden { + display: none !important; + } + + display: flex; + flex-flow: column; +} + diff --git a/www/support/index.html b/www/support/index.html new file mode 100644 index 000000000..79a96c97b --- /dev/null +++ b/www/support/index.html @@ -0,0 +1,12 @@ + + +
+