From 75623a43495ec9c1f69f9b30a0eca97a692e5d71 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Apr 2021 18:08:46 +0530 Subject: [PATCH 01/13] call back with error when creating a login block --- customize.dist/login.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/customize.dist/login.js b/customize.dist/login.js index 8b15cdcd3..7024734ed 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -414,7 +414,10 @@ define([ var blockRequest = Block.serialize(JSON.stringify(toPublish), res.opt.blockKeys); rpc.writeLoginBlock(blockRequest, waitFor(function (e) { - if (e) { return void console.error(e); } + if (e) { + console.error(e); + return void cb(e); + } console.log("blockInfo available at:", blockHash); LocalStore.setBlockHash(blockHash); From 50045c08d04d0d5fb436170cd88f3beb05a5088b Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Apr 2021 18:31:33 +0530 Subject: [PATCH 02/13] WIP restrict registration --- customize.dist/login.js | 4 ++++ customize.dist/pages.js | 11 +++++++++-- customize.dist/template.js | 1 - lib/commands/block.js | 7 ++++++- lib/env.js | 1 + server.js | 1 + www/admin/inner.js | 4 ++-- www/checkup/main.js | 10 +++++++++- www/login/main.js | 10 ---------- www/register/main.js | 18 +++++++----------- www/settings/inner.js | 1 + 11 files changed, 40 insertions(+), 28 deletions(-) diff --git a/customize.dist/login.js b/customize.dist/login.js index 7024734ed..fa950afc2 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -534,6 +534,10 @@ define([ }); }); break; + case 'E_RESTRICTED': + Messages.register_restrictedError = "Registration is restricted on this instance"; // XXX + UI.errorLoadingScreen(Messages.register_restrictedError); // XXX + break; default: // UNHANDLED ERROR hashing = false; UI.errorLoadingScreen(Messages.login_unhandledError); diff --git a/customize.dist/pages.js b/customize.dist/pages.js index f8d7ad15c..43254ad27 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -4,7 +4,8 @@ define([ '/customize/application_config.js', '/customize/messages.js', 'jquery', -], function (h, Language, AppConfig, Msg, $) { + '/api/config', +], function (h, Language, AppConfig, Msg, $, ApiConfig) { var Pages = {}; Pages.setHTML = function (e, html) { @@ -157,10 +158,16 @@ define([ Pages.infopageTopbar = function () { var rightLinks; var username = window.localStorage.getItem('User_name'); + var registerLink; + + if (!ApiConfig.restrictRegistration) { + registerLink = h('a.nav-item.nav-link.cp-register-btn', { href: '/register/'}, Msg.login_register); + } + if (username === null) { rightLinks = [ h('a.nav-item.nav-link.cp-login-btn', { href: '/login/'}, Msg.login_login), - h('a.nav-item.nav-link.cp-register-btn', { href: '/register/'}, Msg.login_register) + registerLink, ]; } else { rightLinks = h('a.nav-item.nav-link.cp-user-btn', { href: '/drive/' }, [ diff --git a/customize.dist/template.js b/customize.dist/template.js index 7721339f3..0e9b3f2b9 100644 --- a/customize.dist/template.js +++ b/customize.dist/template.js @@ -50,7 +50,6 @@ $(function () { } else if (/^\/login\//.test(pathname)) { require([ '/login/main.js' ], function () {}); } else if (/^\/($|^\/index\.html$)/.test(pathname)) { - // TODO use different top bar require([ '/customize/main.js', ], function () {}); } else { require([ '/customize/main.js', ], function () {}); diff --git a/lib/commands/block.js b/lib/commands/block.js index 3a264c167..8d2db4f16 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -86,12 +86,17 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey); }; -Block.writeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS +Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS + var cb = Util.once(Util.mkAsync(_cb)); //console.log(msg); var publicKey = msg[0]; var signature = msg[1]; var block = msg[2]; + if (Env.restrictRegistration /* && notAlreadyRegistered */) { // XXX restricted-registration + return void cb("E_RESTRICTED"); + } + validateLoginBlock(Env, publicKey, signature, block, function (e, validatedBlock) { if (e) { return void cb(e); } if (!(validatedBlock instanceof Uint8Array)) { return void cb('E_INVALID_BLOCK'); } diff --git a/lib/env.js b/lib/env.js index b879d102f..b3ed7cf35 100644 --- a/lib/env.js +++ b/lib/env.js @@ -89,6 +89,7 @@ module.exports.create = function (config) { } }, + restrictRegistration: false, allowSubscriptions: config.allowSubscriptions === true, blockDailyCheck: config.blockDailyCheck === true, diff --git a/server.js b/server.js index 443c16a52..3b49ce4d5 100644 --- a/server.js +++ b/server.js @@ -276,6 +276,7 @@ var serveConfig = makeRouteCache(function (host) { defaultStorageLimit: Env.defaultStorageLimit, maxUploadSize: Env.maxUploadSize, premiumUploadSize: Env.premiumUploadSize, + restrictRegistration: Env.restrictRegistration, // XXX restricted-registration }, null, '\t'), 'obj.httpSafeOrigin = ' + (function () { if (config.httpSafeOrigin) { return '"' + config.httpSafeOrigin + '"'; } diff --git a/www/admin/inner.js b/www/admin/inner.js index 332ff1fe3..844a27aea 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -53,7 +53,7 @@ define([ 'cp-admin-update-limit', 'cp-admin-archive', 'cp-admin-unarchive', - // 'cp-admin-registration', + 'cp-admin-registration', // XXX restricted-registration ], 'quota': [ // Msg.admin_cat_quota 'cp-admin-defaultlimit', @@ -254,7 +254,7 @@ define([ create['registration'] = function () { var key = 'registration'; var $div = makeBlock(key, true); // Msg.admin_registrationHint, .admin_registrationTitle, .admin_registrationButton - var $button = $div.find('button'); + var $button = $div.find('button'); // XXX restricted-registration: a checkbox might be better. it's easy to misinterpret the verb "OPEN" as an adjective var state = APP.instanceStatus.restrictRegistration; if (state) { $button.text(Messages.admin_registrationAllow); diff --git a/www/checkup/main.js b/www/checkup/main.js index 68d35a12e..f02b11c9e 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -243,7 +243,7 @@ define([ opt.keys = secret.keys; opt.channelHex = secret.channel; - var RT, rpc, exists; + var RT, rpc, exists, restricted; nThen(function (waitFor) { Util.fetch(blockUrl, waitFor(function (err) { @@ -285,6 +285,12 @@ define([ // Write block if (exists) { return; } rpc.writeLoginBlock(blockRequest, waitFor(function (e) { + // we should tolerate restricted registration + // and proceed to clean up after any data we've created + if (e === 'E_RESTRICTED') { + restricted = true; + return void cb(true); + } if (e) { waitFor.abort(); console.error("Can't write login block", e); @@ -292,6 +298,7 @@ define([ } })); }).nThen(function (waitFor) { + if (restricted) { return; } // Read block Util.fetch(blockUrl, waitFor(function (e) { if (e) { @@ -303,6 +310,7 @@ define([ }).nThen(function (waitFor) { // Remove block rpc.removeLoginBlock(removeRequest, waitFor(function (e) { + if (restricted) { return; } // an ENOENT is expected in the case of restricted registration, but we call this anyway to clean up any mess from previous tests. if (e) { waitFor.abort(); console.error("Can't remove login block", e); diff --git a/www/login/main.js b/www/login/main.js index 5023e0b19..cb03e17f3 100644 --- a/www/login/main.js +++ b/www/login/main.js @@ -11,21 +11,11 @@ define([ 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', ], function ($, Cryptpad, Login, UI, Realtime, Feedback, LocalStore, Test) { $(function () { - var $main = $('#mainBlock'); var $checkImport = $('#import-recent'); - - // main block is hidden in case javascript is disabled - $main.removeClass('hidden'); - - // Make sure we don't display non-translated content (empty button) - $main.find('#data').removeClass('hidden'); - if (LocalStore.isLoggedIn()) { // already logged in, redirect to drive document.location.href = '/drive/'; return; - } else { - $main.find('#userForm').removeClass('hidden'); } /* Log in UI */ diff --git a/www/register/main.js b/www/register/main.js index b241fd4e5..b86b07b48 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -11,26 +11,22 @@ define([ '/common/common-feedback.js', '/common/outer/local-store.js', '/common/hyperscript.js', + '/api/config', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', -], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h) { +], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h, ApiConfig) { var Messages = Cryptpad.Messages; - + Messages.register_registrationIsClosed = "REGISTRATION IS CLOSED ON THIS INSTANCE."; // XXX $(function () { - var $main = $('#mainBlock'); - - // main block is hidden in case javascript is disabled - $main.removeClass('hidden'); - - // Make sure we don't display non-translated content (empty button) - $main.find('#data').removeClass('hidden'); + if (ApiConfig.restrictRegistration) { + return void UI.alert(Messages.register_registrationIsClosed); // XXX restricted-registration better UI ? + // remove the form and display text instead of an alert that people will probably dismiss? + } if (LocalStore.isLoggedIn()) { // already logged in, redirect to drive document.location.href = '/drive/'; return; - } else { - $main.find('#userForm').removeClass('hidden'); } // text and password input fields diff --git a/www/settings/inner.js b/www/settings/inner.js index 758b90069..4ffdfe530 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -593,6 +593,7 @@ define([ $(form).appendTo($div); var updateBlock = function(data, cb) { +// XXX restricted-registration registered users should be able to change their password even if registration is closed. Include proof of ownership of their existing block when requesting the creation of a new one sframeChan.query('Q_CHANGE_USER_PASSWORD', data, function(err, obj) { if (err || obj.error) { return void cb({ error: err || obj.error }); } cb(obj); From b42adf55768eb6953962fbf625bff598804e5fd1 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Apr 2021 18:35:36 +0530 Subject: [PATCH 03/13] annotate flow for changeUserPassword --- www/common/cryptpad-common.js | 4 ++-- www/common/outer/store-rpc.js | 2 +- www/common/pinpad.js | 2 +- www/common/sframe-common-outer.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 7d6fee8b7..d03371914 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -416,7 +416,7 @@ define([ }); }; - common.writeLoginBlock = function (data, cb) { + common.writeLoginBlock = function (data, cb) { // XXX restricted-registration postMessage('WRITE_LOGIN_BLOCK', data, function (obj) { cb(obj); }); @@ -1746,7 +1746,7 @@ define([ }); }); }; - common.changeUserPassword = function (Crypt, edPublic, data, cb) { + common.changeUserPassword = function (Crypt, edPublic, data, cb) { // XXX restricted-registration if (!edPublic) { return void cb({ error: 'E_NOT_LOGGED_IN' diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 0ad6e3c40..80d14c1c4 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -24,7 +24,7 @@ define([ UPLOAD_COMPLETE: Store.uploadComplete, UPLOAD_STATUS: Store.uploadStatus, UPLOAD_CANCEL: Store.uploadCancel, - WRITE_LOGIN_BLOCK: Store.writeLoginBlock, + WRITE_LOGIN_BLOCK: Store.writeLoginBlock, // XXX restricted-registration REMOVE_LOGIN_BLOCK: Store.removeLoginBlock, PIN_PADS: Store.pinPads, UNPIN_PADS: Store.unpinPads, diff --git a/www/common/pinpad.js b/www/common/pinpad.js index 1e2031088..6bd4e658e 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -220,7 +220,7 @@ var factory = function (Util, Rpc) { }); }; - exp.writeLoginBlock = function (data, cb) { + exp.writeLoginBlock = function (data, cb) { // XXX restricted-registration if (!data) { return void cb('NO_DATA'); } if (!data.publicKey || !data.signature || !data.ciphertext) { console.log(data); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index dde533e9f..f4c0b7bd6 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1557,7 +1557,7 @@ define([ }); sframeChan.on('Q_CHANGE_USER_PASSWORD', function (data, cb) { - Cryptpad.changeUserPassword(Cryptget, edPublic, data, cb); + Cryptpad.changeUserPassword(Cryptget, edPublic, data, cb); // XXX restricted-registration }); sframeChan.on('Q_WRITE_LOGIN_BLOCK', function (data, cb) { From 2436cd2b2f885454e73b29365489acfefc1fb049 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Apr 2021 16:13:15 +0530 Subject: [PATCH 04/13] include 'registrationProof' in WRITE_LOGIN_BLOCK RPC --- www/common/outer/store-rpc.js | 2 +- www/common/pinpad.js | 8 ++++++-- www/common/sframe-common-outer.js | 2 +- www/settings/inner.js | 1 - 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 80d14c1c4..0ad6e3c40 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -24,7 +24,7 @@ define([ UPLOAD_COMPLETE: Store.uploadComplete, UPLOAD_STATUS: Store.uploadStatus, UPLOAD_CANCEL: Store.uploadCancel, - WRITE_LOGIN_BLOCK: Store.writeLoginBlock, // XXX restricted-registration + WRITE_LOGIN_BLOCK: Store.writeLoginBlock, REMOVE_LOGIN_BLOCK: Store.removeLoginBlock, PIN_PADS: Store.pinPads, UNPIN_PADS: Store.unpinPads, diff --git a/www/common/pinpad.js b/www/common/pinpad.js index 6bd4e658e..e1dad6488 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -220,17 +220,21 @@ var factory = function (Util, Rpc) { }); }; - exp.writeLoginBlock = function (data, cb) { // XXX restricted-registration + exp.writeLoginBlock = function (data, cb) { if (!data) { return void cb('NO_DATA'); } if (!data.publicKey || !data.signature || !data.ciphertext) { console.log(data); return void cb("MISSING_PARAMETERS"); } + if (['string', 'undefined'].indexOf(typeof(data.registrationProof)) !== -1) { + return void cb("INVALID_REGISTRATION_PROOF"); + } rpc.send('WRITE_LOGIN_BLOCK', [ data.publicKey, data.signature, - data.ciphertext + data.ciphertext, + data.registrationProof || undefined, ], function (e) { cb(e); }); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index f4c0b7bd6..dde533e9f 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1557,7 +1557,7 @@ define([ }); sframeChan.on('Q_CHANGE_USER_PASSWORD', function (data, cb) { - Cryptpad.changeUserPassword(Cryptget, edPublic, data, cb); // XXX restricted-registration + Cryptpad.changeUserPassword(Cryptget, edPublic, data, cb); }); sframeChan.on('Q_WRITE_LOGIN_BLOCK', function (data, cb) { diff --git a/www/settings/inner.js b/www/settings/inner.js index 4ffdfe530..758b90069 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -593,7 +593,6 @@ define([ $(form).appendTo($div); var updateBlock = function(data, cb) { -// XXX restricted-registration registered users should be able to change their password even if registration is closed. Include proof of ownership of their existing block when requesting the creation of a new one sframeChan.query('Q_CHANGE_USER_PASSWORD', data, function(err, obj) { if (err || obj.error) { return void cb({ error: err || obj.error }); } cb(obj); From 88963915369afa772e3f309ce328b6cb39e176ee Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Apr 2021 17:13:31 +0530 Subject: [PATCH 05/13] refactor writeLoginBlock to account for more asynchronous flow --- lib/commands/block.js | 113 ++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/lib/commands/block.js b/lib/commands/block.js index 8d2db4f16..d3a4a5437 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -86,52 +86,89 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey); }; +var validateAncestorProof = function (Env, proof, newPubKey, cb) { +/* prove that you own an existing block by signing for its publicKey + +we will need: + +1. the publicKey +2. for the old key's block to exist + * path = createLoginBlockPath(Env, oldPublicKey) + * path && FS.readFile(path, err => { !err }) +3. a message signed with that publicKey + +*/ + + cb("E_RESTRICTED"); +}; + Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS var cb = Util.once(Util.mkAsync(_cb)); //console.log(msg); var publicKey = msg[0]; var signature = msg[1]; var block = msg[2]; - - if (Env.restrictRegistration /* && notAlreadyRegistered */) { // XXX restricted-registration - return void cb("E_RESTRICTED"); - } - - validateLoginBlock(Env, publicKey, signature, block, function (e, validatedBlock) { - if (e) { return void cb(e); } - if (!(validatedBlock instanceof Uint8Array)) { return void cb('E_INVALID_BLOCK'); } - - // derive the filepath - var path = createLoginBlockPath(Env, publicKey); - - // make sure the path is valid - if (typeof(path) !== 'string') { - return void cb('E_INVALID_BLOCK_PATH'); + var registrationProof = msg[3]; + + var validatedBlock, parsed, path; + nThen(function (w) { + if (!Env.restrictRegistration) { return; } + if (!registrationProof) { + // we allow users with existing blocks to create new ones + // call back with error if registration is restricted and no proof of an existing block was provided + w.abort(); + return cb("E_RESTRICTED"); } - var parsed = Path.parse(path); - if (!parsed || typeof(parsed.dir) !== 'string') { - return void cb("E_INVALID_BLOCK_PATH_2"); - } - - nThen(function (w) { - // make sure the path to the file exists - Fse.mkdirp(parsed.dir, w(function (e) { - if (e) { - w.abort(); - cb(e); - } - })); - }).nThen(function () { - // actually write the block - - // flow is dumb and I need to guard against this which will never happen - /*:: if (typeof(validatedBlock) === 'undefined') { throw new Error('should never happen'); } */ - /*:: if (typeof(path) === 'undefined') { throw new Error('should never happen'); } */ - Fs.writeFile(path, Buffer.from(validatedBlock), { encoding: "binary", }, function (err) { - if (err) { return void cb(err); } - cb(); - }); +// TODO check that the provided proof was valid +// XXX restricted-registration check whether proof of an existing block was provided + validateAncestorProof(Env, void 0, w(function (err) { + if (err) { + w.abort(); + cb(err); + } + })); + }).nThen(function (w) { + validateLoginBlock(Env, publicKey, signature, block, w(function (e, _validatedBlock) { + if (e) { + w.abort(); + return void cb(e); + } + if (!(_validatedBlock instanceof Uint8Array)) { + w.abort(); + return void cb('E_INVALID_BLOCK'); + } + + validatedBlock = _validatedBlock; + + // derive the filepath + path = createLoginBlockPath(Env, publicKey); + + // make sure the path is valid + if (typeof(path) !== 'string') { + return void cb('E_INVALID_BLOCK_PATH'); + } + + parsed = Path.parse(path); + if (!parsed || typeof(parsed.dir) !== 'string') { + w.abort(); + return void cb("E_INVALID_BLOCK_PATH_2"); + } + })); + }).nThen(function (w) { + // make sure the path to the file exists + Fse.mkdirp(parsed.dir, w(function (e) { + if (e) { + w.abort(); + cb(e); + } + })); + }).nThen(function () { + // actually write the block + Fs.writeFile(path, Buffer.from(validatedBlock), { encoding: "binary", }, function (err) { + if (err) { return void cb(err); } + // XXX log the safeKey to map publicKey <=> block + cb(); }); }); }; From 0124e2c3f4239e6d347220369044cb83899b9b96 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Apr 2021 17:14:19 +0530 Subject: [PATCH 06/13] prepare stubbed proof of block ownership --- www/common/cryptpad-common.js | 7 +++++-- www/common/outer/login-block.js | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index d03371914..6a5a66047 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -416,7 +416,7 @@ define([ }); }; - common.writeLoginBlock = function (data, cb) { // XXX restricted-registration + common.writeLoginBlock = function (data, cb) { postMessage('WRITE_LOGIN_BLOCK', data, function (obj) { cb(obj); }); @@ -1746,7 +1746,7 @@ define([ }); }); }; - common.changeUserPassword = function (Crypt, edPublic, data, cb) { // XXX restricted-registration + common.changeUserPassword = function (Crypt, edPublic, data, cb) { if (!edPublic) { return void cb({ error: 'E_NOT_LOGGED_IN' @@ -1855,6 +1855,9 @@ define([ }; var content = Block.serialize(JSON.stringify(temp), blockKeys); + console.error("OLD AND NEW BLOCK KEYS", oldBlockKeys, blockKeys); + // XXX ignored unless restricted registration is active? + // content.registrationProof = Block.proveAncestor(); console.log("writing new login block"); common.writeLoginBlock(content, waitFor(function (obj) { diff --git a/www/common/outer/login-block.js b/www/common/outer/login-block.js index ef3a38676..46f5158af 100644 --- a/www/common/outer/login-block.js +++ b/www/common/outer/login-block.js @@ -86,6 +86,30 @@ define([ }; }; + Block.proveAncestor = function (O /* oldBlockKeys */, N /* newBlockKeys */) { + N = N; +/* + var registrationProof; + var registrationSig; + if (keys.previous) { + // XXX restricted-registration + // sign the publicKey of the new key with the old key + + // your existing block's publicKey + // the new block's publicKey + // some proof of recency to prevent replays? + // a signature of the whole thing + + // registrationProof = [oldPublicKey, newPublicKey]; + // registrationSig = sign(registrationProof, oldPrivateKey); + } +*/ + +// needed for password change with restricted registration +//registrationProof: registrationProof? Nacl.util.encodeBase64(registrationProof): undefined, +//registrationSig: registrationSig? Nacl.util.encode + }; + Block.remove = function (keys) { // sign the hash of the text 'DELETE_BLOCK' var sig = Nacl.sign.detached(Nacl.hash( From 18c73ad282e0d49aeb9268f6a59ba50ca3faf3ee Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 28 Apr 2021 11:16:55 +0530 Subject: [PATCH 07/13] WIP working password change with restricted registration --- lib/commands/block.js | 56 +++++++++++++++++++++------------ www/common/cryptpad-common.js | 2 +- www/common/outer/login-block.js | 28 ++++++----------- www/common/pinpad.js | 2 +- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/lib/commands/block.js b/lib/commands/block.js index d3a4a5437..a398359d2 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -86,20 +86,37 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey); }; -var validateAncestorProof = function (Env, proof, newPubKey, cb) { -/* prove that you own an existing block by signing for its publicKey - -we will need: - -1. the publicKey -2. for the old key's block to exist - * path = createLoginBlockPath(Env, oldPublicKey) - * path && FS.readFile(path, err => { !err }) -3. a message signed with that publicKey - -*/ - - cb("E_RESTRICTED"); +var validateAncestorProof = function (Env, proof, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); +/* prove that you own an existing block by signing for its publicKey */ + try { + var parsed = JSON.parse(proof); + var pub = parsed[0]; + var u8_pub = Nacl.util.decodeBase64(pub); + var sig = parsed[1]; + var u8_sig = Nacl.util.decodeBase64(sig); + var valid = false; + nThen(function (w) { + // XXX restricted-registration do this in a worker + valid = Nacl.sign.detached.verify(u8_pub, u8_sig, u8_pub); + if (!valid) { + w.abort(); + return void cb('E_INVALID_ANCESTOR_PROOF'); + } + // else fall through to next step + }).nThen(function (w) { + var path = createLoginBlockPath(Env, pub); + Fs.access(path, Fs.constants.F_OK, w(function (err) { + if (!err) { return; } + w.abort(); // else + return void cb("E_MISSING_ANCESTOR"); + })); + }).nThen(function () { + cb(void 0, valid); + }); + } catch (err) { + return void cb(err); + } }; Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS @@ -119,14 +136,13 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS w.abort(); return cb("E_RESTRICTED"); } - -// TODO check that the provided proof was valid -// XXX restricted-registration check whether proof of an existing block was provided - validateAncestorProof(Env, void 0, w(function (err) { - if (err) { + validateAncestorProof(Env, registrationProof, w(function (err, validated) { + if (err || !validated) { // double check that it was validated w.abort(); - cb(err); + // XXX Log + return void cb("E_RESTRICTED"); // XXX restricted-registration we can provide more descriptive errors if we check that the client will understand them } + // else fall through to the next block })); }).nThen(function (w) { validateLoginBlock(Env, publicKey, signature, block, w(function (e, _validatedBlock) { diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 6a5a66047..0da28a2bc 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1857,7 +1857,7 @@ define([ var content = Block.serialize(JSON.stringify(temp), blockKeys); console.error("OLD AND NEW BLOCK KEYS", oldBlockKeys, blockKeys); // XXX ignored unless restricted registration is active? - // content.registrationProof = Block.proveAncestor(); + content.registrationProof = Block.proveAncestor(oldBlockKeys); console.log("writing new login block"); common.writeLoginBlock(content, waitFor(function (obj) { diff --git a/www/common/outer/login-block.js b/www/common/outer/login-block.js index 46f5158af..e181dbf3f 100644 --- a/www/common/outer/login-block.js +++ b/www/common/outer/login-block.js @@ -88,26 +88,16 @@ define([ Block.proveAncestor = function (O /* oldBlockKeys */, N /* newBlockKeys */) { N = N; -/* - var registrationProof; - var registrationSig; - if (keys.previous) { - // XXX restricted-registration - // sign the publicKey of the new key with the old key - - // your existing block's publicKey - // the new block's publicKey - // some proof of recency to prevent replays? - // a signature of the whole thing - - // registrationProof = [oldPublicKey, newPublicKey]; - // registrationSig = sign(registrationProof, oldPrivateKey); + var u8_pub = Util.find(O, ['sign', 'publicKey']); + var u8_secret = Util.find(O, ['sign', 'secretKey']); + try { + // sign your old publicKey with your old privateKey + var u8_sig = Nacl.sign.detached(u8_pub, u8_secret); + // return an array with the sig and the pubkey + return JSON.stringify([u8_pub, u8_sig].map(Nacl.util.encodeBase64)); + } catch (err) { + throw err; // XXX restricted-registration } -*/ - -// needed for password change with restricted registration -//registrationProof: registrationProof? Nacl.util.encodeBase64(registrationProof): undefined, -//registrationSig: registrationSig? Nacl.util.encode }; Block.remove = function (keys) { diff --git a/www/common/pinpad.js b/www/common/pinpad.js index e1dad6488..7e9cd4ee2 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -226,7 +226,7 @@ var factory = function (Util, Rpc) { console.log(data); return void cb("MISSING_PARAMETERS"); } - if (['string', 'undefined'].indexOf(typeof(data.registrationProof)) !== -1) { + if (['string', 'undefined'].indexOf(typeof(data.registrationProof)) === -1) { return void cb("INVALID_REGISTRATION_PROOF"); } From 8546e7b317404d3dac264af5534e44b8f5f3fbfc Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 28 Apr 2021 14:27:01 +0530 Subject: [PATCH 08/13] hide references to registration if registration is disabled --- customize.dist/pages/features.js | 2 +- customize.dist/pages/login.js | 10 ++- customize.dist/pages/register.js | 103 +++++++++++++++++-------------- www/common/common-ui-elements.js | 18 +++--- www/register/main.js | 8 +-- 5 files changed, 77 insertions(+), 64 deletions(-) diff --git a/customize.dist/pages/features.js b/customize.dist/pages/features.js index ff67c6912..ee82d5d67 100644 --- a/customize.dist/pages/features.js +++ b/customize.dist/pages/features.js @@ -146,7 +146,7 @@ define([ ]), ]); var availableFeatures = - (Config.allowSubscriptions && accounts.upgradeURL) ? + (Config.allowSubscriptions && accounts.upgradeURL && !Config.restrictRegistration) ? [anonymousFeatures, registeredFeatures, premiumFeatures] : [anonymousFeatures, registeredFeatures]; diff --git a/customize.dist/pages/login.js b/customize.dist/pages/login.js index 5322c663f..45d3a3707 100644 --- a/customize.dist/pages/login.js +++ b/customize.dist/pages/login.js @@ -2,8 +2,9 @@ define([ '/common/hyperscript.js', '/common/common-interface.js', '/customize/messages.js', - '/customize/pages.js' -], function (h, UI, Msg, Pages) { + '/customize/pages.js', + '/api/config', +], function (h, UI, Msg, Pages, Config) { return function () { return [h('div#cp-main', [ Pages.infopageTopbar(), @@ -32,7 +33,10 @@ define([ ]), h('div.extra', [ h('button.login', Msg.login_login), - h('button#register.cp-secondary', Msg.login_register) + (Config.restrictRegistration? + undefined: + h('button#register.cp-secondary', Msg.login_register) + ) ]) ]), h('div.col-md-3') diff --git a/customize.dist/pages/register.js b/customize.dist/pages/register.js index 1cdc6e356..7af93853b 100644 --- a/customize.dist/pages/register.js +++ b/customize.dist/pages/register.js @@ -16,54 +16,67 @@ define([ tabindex: '-1', }); - return [h('div#cp-main', [ - Pages.infopageTopbar(), - h('div.container.cp-container', [ - h('div.row.cp-page-title', h('h1', Msg.register_header)), - h('div.row.cp-register-det', [ - h('div#data.hidden.col-md-6', [ - h('h2', Msg.register_notes_title), - Pages.setHTML(h('div.cp-register-notes'), Msg.register_notes) + + var frame = function (content) { + return [ + h('div#cp-main', [ + Pages.infopageTopbar(), + h('div.container.cp-container', [ + h('div.row.cp-page-title', h('h1', Msg.register_header)), + h('div.row.cp-register-det', content), + ]), ]), - h('div.cp-reg-form.col-md-6', [ - h('img.img-fluid', { - src: '/customize/images/swallow-the-key.png?' + urlArgs + Pages.infopageFooter(), + ]; + }; + + if (Config.restrictRegistration) { // XXX restricted-registration + Msg.register_closed = "This instance does not allow registration at the moment."; + return frame([ + h('h4', Msg.register_closed) + ]); + } + + return frame([ + h('div#data.hidden.col-md-6', [ + h('h2', Msg.register_notes_title), + Pages.setHTML(h('div.cp-register-notes'), Msg.register_notes) + ]), + h('div.cp-reg-form.col-md-6', [ + h('img.img-fluid', { + src: '/customize/images/swallow-the-key.png?' + urlArgs + }), + h('div#userForm.form-group.hidden', [ + h('a', { + href: '/features.html' + }, Msg.register_whyRegister), + h('input.form-control#username', { + type: 'text', + autocomplete: 'off', + autocorrect: 'off', + autocapitalize: 'off', + spellcheck: false, + placeholder: Msg.login_username, + autofocus: true, }), - h('div#userForm.form-group.hidden', [ - h('a', { - href: '/features.html' - }, Msg.register_whyRegister), - h('input.form-control#username', { - type: 'text', - autocomplete: 'off', - autocorrect: 'off', - autocapitalize: 'off', - spellcheck: false, - placeholder: Msg.login_username, - autofocus: true, - }), - h('input.form-control#password', { - type: 'password', - placeholder: Msg.login_password, - }), - h('input.form-control#password-confirm', { - type: 'password', - placeholder: Msg.login_confirm, - }), - h('div.checkbox-container', [ - UI.createCheckbox('import-recent', Msg.register_importRecent, true) - ]), - h('div.checkbox-container', [ - tos, - ]), - h('button#register', Msg.login_register) - ]) - ]), - ]), + h('input.form-control#password', { + type: 'password', + placeholder: Msg.login_password, + }), + h('input.form-control#password-confirm', { + type: 'password', + placeholder: Msg.login_confirm, + }), + h('div.checkbox-container', [ + UI.createCheckbox('import-recent', Msg.register_importRecent, true) + ]), + h('div.checkbox-container', [ + tos, + ]), + h('button#register', Msg.login_register) + ]) ]), - - Pages.infopageFooter(), - ])]; + ]); }; }); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 471bd5011..0f070595b 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1833,14 +1833,16 @@ define([ Common.setLoginRedirect('login'); }, }); - options.push({ - tag: 'a', - attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'}, - content: h('span', Messages.login_register), - action: function () { - Common.setLoginRedirect('register'); - }, - }); + if (!Config.restrictRegistration) { + options.push({ + tag: 'a', + attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'}, + content: h('span', Messages.login_register), + action: function () { + Common.setLoginRedirect('register'); + }, + }); + } } var $icon = $('', {'class': 'fa fa-user-secret'}); //var $userbig = $('', {'class': 'big'}).append($displayedName.clone()); diff --git a/www/register/main.js b/www/register/main.js index b86b07b48..2c66aebb4 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -11,18 +11,12 @@ define([ '/common/common-feedback.js', '/common/outer/local-store.js', '/common/hyperscript.js', - '/api/config', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', -], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h, ApiConfig) { +], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h) { var Messages = Cryptpad.Messages; Messages.register_registrationIsClosed = "REGISTRATION IS CLOSED ON THIS INSTANCE."; // XXX $(function () { - if (ApiConfig.restrictRegistration) { - return void UI.alert(Messages.register_registrationIsClosed); // XXX restricted-registration better UI ? - // remove the form and display text instead of an alert that people will probably dismiss? - } - if (LocalStore.isLoggedIn()) { // already logged in, redirect to drive document.location.href = '/drive/'; From a813b6f2269dce04a9fb509e6bd2dcc28ce4e914 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 28 Apr 2021 16:28:33 +0530 Subject: [PATCH 09/13] remove or adjust some comments --- customize.dist/pages/register.js | 4 ++-- www/admin/inner.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/customize.dist/pages/register.js b/customize.dist/pages/register.js index 7af93853b..46ffc7f7a 100644 --- a/customize.dist/pages/register.js +++ b/customize.dist/pages/register.js @@ -30,8 +30,8 @@ define([ ]; }; - if (Config.restrictRegistration) { // XXX restricted-registration - Msg.register_closed = "This instance does not allow registration at the moment."; + if (Config.restrictRegistration) { + Msg.register_closed = "This instance does not allow registration at the moment."; // XXX return frame([ h('h4', Msg.register_closed) ]); diff --git a/www/admin/inner.js b/www/admin/inner.js index 844a27aea..f4ec5a795 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -53,7 +53,7 @@ define([ 'cp-admin-update-limit', 'cp-admin-archive', 'cp-admin-unarchive', - 'cp-admin-registration', // XXX restricted-registration + 'cp-admin-registration', ], 'quota': [ // Msg.admin_cat_quota 'cp-admin-defaultlimit', From 15a8284a307e1cd0ee89820787d9b6ccd77edff0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 29 Apr 2021 07:09:16 +0530 Subject: [PATCH 10/13] add some logging and avoid some errors --- lib/commands/block.js | 22 +++++++++++++++------- server.js | 2 +- www/common/outer/login-block.js | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/commands/block.js b/lib/commands/block.js index a398359d2..288089a16 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -112,7 +112,7 @@ var validateAncestorProof = function (Env, proof, _cb) { return void cb("E_MISSING_ANCESTOR"); })); }).nThen(function () { - cb(void 0, valid); + cb(void 0, pub); }); } catch (err) { return void cb(err); @@ -126,6 +126,7 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS var signature = msg[1]; var block = msg[2]; var registrationProof = msg[3]; + var previousKey; var validatedBlock, parsed, path; nThen(function (w) { @@ -136,13 +137,15 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS w.abort(); return cb("E_RESTRICTED"); } - validateAncestorProof(Env, registrationProof, w(function (err, validated) { - if (err || !validated) { // double check that it was validated + validateAncestorProof(Env, registrationProof, w(function (err, provenKey) { + if (err || !provenKey) { // double check that a key was validated w.abort(); - // XXX Log - return void cb("E_RESTRICTED"); // XXX restricted-registration we can provide more descriptive errors if we check that the client will understand them + Env.Log.warn('BLOCK_REJECTED_INVALID_ANCESTOR', { + error: err, + }); + return void cb("E_RESTRICTED"); } - // else fall through to the next block + previousKey = provenKey; })); }).nThen(function (w) { validateLoginBlock(Env, publicKey, signature, block, w(function (e, _validatedBlock) { @@ -183,7 +186,12 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS // actually write the block Fs.writeFile(path, Buffer.from(validatedBlock), { encoding: "binary", }, function (err) { if (err) { return void cb(err); } - // XXX log the safeKey to map publicKey <=> block + Env.Log.info('BLOCK_WRITE_BY_OWNER', { + safeKey: safeKey, + blockId: publicKey, + isChange: Boolean(registrationProof), + previousKey: previousKey, + }); cb(); }); }); diff --git a/server.js b/server.js index 3b49ce4d5..4a107b785 100644 --- a/server.js +++ b/server.js @@ -276,7 +276,7 @@ var serveConfig = makeRouteCache(function (host) { defaultStorageLimit: Env.defaultStorageLimit, maxUploadSize: Env.maxUploadSize, premiumUploadSize: Env.premiumUploadSize, - restrictRegistration: Env.restrictRegistration, // XXX restricted-registration + restrictRegistration: Env.restrictRegistration, }, null, '\t'), 'obj.httpSafeOrigin = ' + (function () { if (config.httpSafeOrigin) { return '"' + config.httpSafeOrigin + '"'; } diff --git a/www/common/outer/login-block.js b/www/common/outer/login-block.js index e181dbf3f..9950e18af 100644 --- a/www/common/outer/login-block.js +++ b/www/common/outer/login-block.js @@ -96,7 +96,7 @@ define([ // return an array with the sig and the pubkey return JSON.stringify([u8_pub, u8_sig].map(Nacl.util.encodeBase64)); } catch (err) { - throw err; // XXX restricted-registration + return void console.error(err); } }; From 729d51fb9b6f73c5c8b94c0d81add8bfc3eddd0e Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 29 Apr 2021 13:32:19 +0530 Subject: [PATCH 11/13] update block storage APIs --- customize.dist/login.js | 2 +- lib/commands/block.js | 56 +++++++++++++++++++++------------ lib/historyKeeper.js | 1 + lib/storage/blob.js | 2 +- lib/workers/db-worker.js | 6 ++++ lib/workers/index.js | 7 +++++ www/common/cryptpad-common.js | 16 +++++++--- www/common/outer/async-store.js | 42 +++++++++++++++++++------ www/common/outer/login-block.js | 13 ++++++++ 9 files changed, 109 insertions(+), 36 deletions(-) diff --git a/customize.dist/login.js b/customize.dist/login.js index fa950afc2..9cb9765fb 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -392,7 +392,7 @@ define([ // send an RPC to store the block which you created. console.log("initializing rpc interface"); - Pinpad.create(RT.network, RT.proxy, waitFor(function (e, _rpc) { + Pinpad.create(RT.network, Block.keysToRPCFormat(res.opt.blockKeys), waitFor(function (e, _rpc) { if (e) { waitFor.abort(); console.error(e); // INVALID_KEYS diff --git a/lib/commands/block.js b/lib/commands/block.js index 288089a16..8180cb68e 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -86,7 +86,7 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey); }; -var validateAncestorProof = function (Env, proof, _cb) { +Block.validateAncestorProof = function (Env, proof, _cb) { var cb = Util.once(Util.mkAsync(_cb)); /* prove that you own an existing block by signing for its publicKey */ try { @@ -97,7 +97,6 @@ var validateAncestorProof = function (Env, proof, _cb) { var u8_sig = Nacl.util.decodeBase64(sig); var valid = false; nThen(function (w) { - // XXX restricted-registration do this in a worker valid = Nacl.sign.detached.verify(u8_pub, u8_sig, u8_pub); if (!valid) { w.abort(); @@ -130,14 +129,23 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS var validatedBlock, parsed, path; nThen(function (w) { + if (Util.escapeKeyCharacters(publicKey) !== safeKey) { + w.abort(); + return void cb("INCORRECT_KEY"); + } + }).nThen(function (w) { if (!Env.restrictRegistration) { return; } if (!registrationProof) { // we allow users with existing blocks to create new ones // call back with error if registration is restricted and no proof of an existing block was provided w.abort(); + Env.Log.info("BLOCK_REJECTED_REGISTRATION", { + safeKey: safeKey, + publicKey: publicKey, + }); return cb("E_RESTRICTED"); } - validateAncestorProof(Env, registrationProof, w(function (err, provenKey) { + Env.validateAncestorProof(registrationProof, w(function (err, provenKey) { if (err || !provenKey) { // double check that a key was validated w.abort(); Env.Log.warn('BLOCK_REJECTED_INVALID_ANCESTOR', { @@ -191,6 +199,7 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS blockId: publicKey, isChange: Boolean(registrationProof), previousKey: previousKey, + path: path, }); cb(); }); @@ -212,26 +221,33 @@ Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS var signature = msg[1]; var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant - validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { - if (e) { return void cb(e); } - // derive the filepath - var path = createLoginBlockPath(Env, publicKey); - - // make sure the path is valid - if (typeof(path) !== 'string') { - return void cb('E_INVALID_BLOCK_PATH'); + nThen(function (w) { + if (Util.escapeKeyCharacters(publicKey) !== safeKey) { + w.abort(); + return void cb("INCORRECT_KEY"); } + }).nThen(function () { + validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { + if (e) { return void cb(e); } + // derive the filepath + var path = createLoginBlockPath(Env, publicKey); - // FIXME COLDSTORAGE - Fs.unlink(path, function (err) { - Env.Log.info('DELETION_BLOCK_BY_OWNER_RPC', { - publicKey: publicKey, - path: path, - status: err? String(err): 'SUCCESS', - }); + // make sure the path is valid + if (typeof(path) !== 'string') { + return void cb('E_INVALID_BLOCK_PATH'); + } - if (err) { return void cb(err); } - cb(); + // FIXME COLDSTORAGE + Fs.unlink(path, function (err) { + Env.Log.info('DELETION_BLOCK_BY_OWNER_RPC', { + publicKey: publicKey, + path: path, + status: err? String(err): 'SUCCESS', + }); + + if (err) { return void cb(err); } + cb(); + }); }); }); }; diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index fb7a5ebc8..daa041b5f 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -158,6 +158,7 @@ module.exports.create = function (Env, cb) { pinPath: Env.paths.pin, filePath: Env.paths.data, archivePath: Env.paths.archive, + blockPath: Env.paths.block, inactiveTime: Env.inactiveTime, archiveRetentionTime: Env.archiveRetentionTime, diff --git a/lib/storage/blob.js b/lib/storage/blob.js index 044eeaeaa..e5c7a2fce 100644 --- a/lib/storage/blob.js +++ b/lib/storage/blob.js @@ -295,7 +295,7 @@ var owned_upload_complete = function (Env, safeKey, id, cb) { // removeBlob var remove = function (Env, blobId, cb) { var blobPath = makeBlobPath(Env, blobId); - Fs.unlink(blobPath, cb); // TODO COLDSTORAGE + Fs.unlink(blobPath, cb); }; // removeProof diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 65f13d23b..8585cc3f6 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -4,6 +4,7 @@ const HK = require("../hk-util"); const Store = require("../storage/file"); const BlobStore = require("../storage/blob"); +const Block = require("../commands/block"); const Util = require("../common-util"); const nThen = require("nthen"); const Meta = require("../metadata"); @@ -47,6 +48,7 @@ const init = function (config, _cb) { Env.paths = { pin: config.pinPath, + block: config.blockPath, }; Env.inactiveTime = config.inactiveTime; @@ -688,6 +690,10 @@ COMMANDS.HASH_CHANNEL_LIST = function (data, cb) { cb(void 0, hash); }; +COMMANDS.VALIDATE_ANCESTOR_PROOF = function (data, cb) { + Block.validateAncestorProof(Env, data && data.proof, cb); +}; + process.on('message', function (data) { if (!data || !data.txid || !data.pid) { return void process.send({ diff --git a/lib/workers/index.js b/lib/workers/index.js index 25c18d947..85c66eeb5 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -444,6 +444,13 @@ Workers.initialize = function (Env, config, _cb) { }, cb); }; + Env.validateAncestorProof = function (proof, cb) { + sendCommand({ + command: 'VALIDATE_ANCESTOR_PROOF', + proof: proof, + }, cb); + }; + cb(void 0); }); }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 0da28a2bc..27eb48787 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1735,6 +1735,7 @@ define([ var removeData = obj.Block.remove(blockKeys); postMessage("DELETE_ACCOUNT", { + keys: Block.keysToRPCFormat(blockKeys), removeData: removeData }, function (obj) { if (obj.state) { @@ -1856,11 +1857,15 @@ define([ var content = Block.serialize(JSON.stringify(temp), blockKeys); console.error("OLD AND NEW BLOCK KEYS", oldBlockKeys, blockKeys); - // XXX ignored unless restricted registration is active? content.registrationProof = Block.proveAncestor(oldBlockKeys); console.log("writing new login block"); - common.writeLoginBlock(content, waitFor(function (obj) { + + var data = { + keys: Block.keysToRPCFormat(blockKeys), + content: content, + }; + common.writeLoginBlock(data, waitFor(function (obj) { if (obj && obj.error) { waitFor.abort(); return void cb(obj); @@ -1878,8 +1883,11 @@ define([ // Remove block hash if (blockHash) { console.log('removing old login block'); - var removeData = Block.remove(oldBlockKeys); - common.removeLoginBlock(removeData, waitFor(function (obj) { + var data = { + keys: Block.keysToRPCFormat(oldBlockKeys), // { edPrivate, edPublic } + content: Block.remove(oldBlockKeys), + }; + common.removeLoginBlock(data, waitFor(function (obj) { if (obj && obj.error) { return void console.error(obj.error); } })); } diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 3eb983fdb..21bc77798 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -451,19 +451,33 @@ define([ }; Store.writeLoginBlock = function (clientId, data, cb) { - store.rpc.writeLoginBlock(data, function (e, res) { - cb({ - error: e, - data: res + Pinpad.create(store.network, data && data.keys, function (err, rpc) { + if (err) { + return void cb({ + error: err, + }); + } + rpc.writeLoginBlock(data && data.content, function (e, res) { + cb({ + error: e, + data: res + }); }); }); }; Store.removeLoginBlock = function (clientId, data, cb) { - store.rpc.removeLoginBlock(data, function (e, res) { - cb({ - error: e, - data: res + Pinpad.create(store.network, data && data.keys, function (err, rpc) { + if (err) { + return void cb({ + error: err, + }); + } + rpc.removeLoginBlock(data && data.content, function (e, res) { + cb({ + error: e, + data: res + }); }); }); }; @@ -815,6 +829,7 @@ define([ Store.deleteAccount = function (clientId, data, cb) { var edPublic = store.proxy.edPublic; var removeData = data && data.removeData; + var rpcKeys = data && data.keys; Store.anonRpcMsg(clientId, { msg: 'GET_METADATA', data: store.driveChannel @@ -845,8 +860,15 @@ define([ }, waitFor()); }).nThen(function (waitFor) { if (!removeData) { return; } - // Delete the block. Don't abort if it fails, it doesn't leak any data. - store.rpc.removeLoginBlock(removeData, waitFor()); + var done = waitFor(); + Pinpad.create(store.network, rpcKeys, function (err, rpc) { + if (err) { + console.error(err); + return void done(); + } + // Delete the block. Don't abort if it fails, it doesn't leak any data. + rpc.removeLoginBlock(removeData, done); + }); }).nThen(function () { // Log out current worker postMessage(clientId, "DELETE_ACCOUNT", token, function () {}); diff --git a/www/common/outer/login-block.js b/www/common/outer/login-block.js index 9950e18af..0d75fdfca 100644 --- a/www/common/outer/login-block.js +++ b/www/common/outer/login-block.js @@ -40,6 +40,19 @@ define([ }; }; + Block.keysToRPCFormat = function (keys) { + try { + var sign = keys.sign; + return { + edPrivate: Nacl.util.encodeBase64(sign.secretKey), + edPublic: Nacl.util.encodeBase64(sign.publicKey), + }; + } catch (err) { + console.error(err); + return; + } + }; + // (UTF8 content, keys object) => Uint8Array block Block.encrypt = function (version, content, keys) { var u8 = Nacl.util.decodeUTF8(content); From 67a909711d0cf6528da97aa564acecbf300ffb47 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 29 Apr 2021 14:39:22 +0530 Subject: [PATCH 12/13] slightly better layout for disabled register page --- customize.dist/pages/register.js | 84 ++++++++++--------- .../src/less2/pages/page-register.less | 3 + 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/customize.dist/pages/register.js b/customize.dist/pages/register.js index 46ffc7f7a..b8f82116d 100644 --- a/customize.dist/pages/register.js +++ b/customize.dist/pages/register.js @@ -23,8 +23,8 @@ define([ Pages.infopageTopbar(), h('div.container.cp-container', [ h('div.row.cp-page-title', h('h1', Msg.register_header)), - h('div.row.cp-register-det', content), - ]), + //h('div.row.cp-register-det', content), + ].concat(content)), ]), Pages.infopageFooter(), ]; @@ -33,49 +33,53 @@ define([ if (Config.restrictRegistration) { Msg.register_closed = "This instance does not allow registration at the moment."; // XXX return frame([ - h('h4', Msg.register_closed) + h('div.cp-restricted-registration', [ + h('p', Msg.register_closed), + ]) ]); } return frame([ - h('div#data.hidden.col-md-6', [ - h('h2', Msg.register_notes_title), - Pages.setHTML(h('div.cp-register-notes'), Msg.register_notes) - ]), - h('div.cp-reg-form.col-md-6', [ - h('img.img-fluid', { - src: '/customize/images/swallow-the-key.png?' + urlArgs - }), - h('div#userForm.form-group.hidden', [ - h('a', { - href: '/features.html' - }, Msg.register_whyRegister), - h('input.form-control#username', { - type: 'text', - autocomplete: 'off', - autocorrect: 'off', - autocapitalize: 'off', - spellcheck: false, - placeholder: Msg.login_username, - autofocus: true, - }), - h('input.form-control#password', { - type: 'password', - placeholder: Msg.login_password, - }), - h('input.form-control#password-confirm', { - type: 'password', - placeholder: Msg.login_confirm, + h('div.row.cp-register-det', [ + h('div#data.hidden.col-md-6', [ + h('h2', Msg.register_notes_title), + Pages.setHTML(h('div.cp-register-notes'), Msg.register_notes) + ]), + h('div.cp-reg-form.col-md-6', [ + h('img.img-fluid', { + src: '/customize/images/swallow-the-key.png?' + urlArgs }), - h('div.checkbox-container', [ - UI.createCheckbox('import-recent', Msg.register_importRecent, true) - ]), - h('div.checkbox-container', [ - tos, - ]), - h('button#register', Msg.login_register) - ]) - ]), + h('div#userForm.form-group.hidden', [ + h('a', { + href: '/features.html' + }, Msg.register_whyRegister), + h('input.form-control#username', { + type: 'text', + autocomplete: 'off', + autocorrect: 'off', + autocapitalize: 'off', + spellcheck: false, + placeholder: Msg.login_username, + autofocus: true, + }), + h('input.form-control#password', { + type: 'password', + placeholder: Msg.login_password, + }), + h('input.form-control#password-confirm', { + type: 'password', + placeholder: Msg.login_confirm, + }), + h('div.checkbox-container', [ + UI.createCheckbox('import-recent', Msg.register_importRecent, true) + ]), + h('div.checkbox-container', [ + tos, + ]), + h('button#register', Msg.login_register) + ]) + ]), + ]) ]); }; diff --git a/customize.dist/src/less2/pages/page-register.less b/customize.dist/src/less2/pages/page-register.less index ce4309822..1ce8bbd5f 100644 --- a/customize.dist/src/less2/pages/page-register.less +++ b/customize.dist/src/less2/pages/page-register.less @@ -52,6 +52,9 @@ } } + .cp-restricted-registration { + text-align: center !important; + } .cp-register-det { #data { From c7a731d30c1bbae2bbd6079fd0517aac40e466db Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 4 May 2021 13:45:44 +0530 Subject: [PATCH 13/13] remove hardcoded translation --- customize.dist/login.js | 3 +-- customize.dist/pages/register.js | 3 +-- www/register/main.js | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/customize.dist/login.js b/customize.dist/login.js index 9cb9765fb..74e5e86f6 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -535,8 +535,7 @@ define([ }); break; case 'E_RESTRICTED': - Messages.register_restrictedError = "Registration is restricted on this instance"; // XXX - UI.errorLoadingScreen(Messages.register_restrictedError); // XXX + UI.errorLoadingScreen(Messages.register_registrationIsClosed); break; default: // UNHANDLED ERROR hashing = false; diff --git a/customize.dist/pages/register.js b/customize.dist/pages/register.js index b8f82116d..cf05defa5 100644 --- a/customize.dist/pages/register.js +++ b/customize.dist/pages/register.js @@ -31,10 +31,9 @@ define([ }; if (Config.restrictRegistration) { - Msg.register_closed = "This instance does not allow registration at the moment."; // XXX return frame([ h('div.cp-restricted-registration', [ - h('p', Msg.register_closed), + h('p', Msg.register_registrationIsClosed), ]) ]); } diff --git a/www/register/main.js b/www/register/main.js index 2c66aebb4..4c63e4482 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -15,7 +15,6 @@ define([ 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', ], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h) { var Messages = Cryptpad.Messages; - Messages.register_registrationIsClosed = "REGISTRATION IS CLOSED ON THIS INSTANCE."; // XXX $(function () { if (LocalStore.isLoggedIn()) { // already logged in, redirect to drive