From 1879c1829c670777dbe7778f028d503d4582d060 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Apr 2018 19:03:58 +0200 Subject: [PATCH] Add passwod prompt to access protected pads --- customize.dist/loading.js | 32 ++++++++ .../src/less2/include/creation.less | 34 ++++++-- customize.dist/translations/messages.fr.js | 6 ++ customize.dist/translations/messages.js | 6 ++ www/code/inner.js | 1 + www/common/common-hash.js | 34 ++++---- www/common/common-interface.js | 8 +- www/common/common-ui-elements.js | 81 +++++++++++++++++-- www/common/cryptget.js | 2 - www/common/cryptpad-common.js | 44 ++++++++-- www/common/mergeDrive.js | 14 +--- www/common/outer/async-store.js | 12 ++- www/common/outer/store-rpc.js | 3 + www/common/sframe-common-outer.js | 59 ++++++++++---- www/common/sframe-common.js | 10 +++ www/common/sframe-protocol.js | 4 + www/poll/inner.js | 2 - 17 files changed, 282 insertions(+), 70 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index a2c1dcdfc..80e064a67 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -81,6 +81,38 @@ define([], function () { text-align: center; display: none; } +#cp-loading-password-prompt { + font-size: 18px; +} +#cp-loading-password-prompt .cp-password-error { + color: white; + background: #9e0000; + padding: 5px; + margin-bottom: 15px; +} +#cp-loading-password-prompt .cp-password-info { + text-align: left; + margin-bottom: 15px; +} +#cp-loading-password-prompt .cp-password-form { + display: flex; + justify-content: space-around; +} +#cp-loading-password-prompt .cp-password-form * { + background-color: #4591c4; + color: white; + border: 1px solid #4591c4; +} +#cp-loading-password-prompt .cp-password-form input { + flex: 1; + margin-right: 15px; + padding: 0 5px; + min-width: 0; + text-overflow: ellipsis; +} +#cp-loading-password-prompt .cp-password-form button:hover { + background-color: #326599; +} #cp-loading .cp-loading-spinner-container { position: relative; height: 100px; diff --git a/customize.dist/src/less2/include/creation.less b/customize.dist/src/less2/include/creation.less index b2f6fa46b..f37fbdb35 100644 --- a/customize.dist/src/less2/include/creation.less +++ b/customize.dist/src/less2/include/creation.less @@ -144,16 +144,18 @@ max-height: 100px; } } + + input, select { + font-size: 14px; + border: 1px solid @colortheme_form-border; + height: 26px; + background-color: @colortheme_form-bg; + color: @colortheme_form-color; + } + .cp-creation-expire { .cp-creation-expire-picker { text-align: center; - input, select { - font-size: 14px; - border: 1px solid @colortheme_form-border; - height: 26px; - background-color: @colortheme_form-bg; - color: @colortheme_form-color; - } input { width: 50px; margin: 0 5px; @@ -171,6 +173,22 @@ } } } + .cp-creation-password { + .cp-creation-password-picker { + text-align: center; + input { + width: 150px; + } + } + &.active { + label { + flex: unset; + } + .cp-creation-slider { + flex: 1; + } + } + } .cp-creation-settings { button { margin: 0; @@ -314,7 +332,7 @@ width: 95%; margin: 10px auto; } - .cp-creation-expire { + .cp-creation-expire, .cp-creation-password { &.active { label { flex: 1; diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 86f960bcb..e43cad6a1 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -1098,6 +1098,12 @@ define(function () { out.creation_newPadModalDescriptionAdvanced = "Cochez la case si vous souhaitez voir l'écran de création de pads (pour les pads avec propriétaire ou à durée de vie). Vous pouvez appuyer sur Espace pour changer sa valeur."; out.creation_newPadModalAdvanced = "Afficher l'écran de création de pads"; + // Password prompt on the loadind screen + out.password_info = "Le pad auquel vous essayez d'accéder est protégé par un mot de passe. Entrez le bon mot de passe pour accéder à son contenu."; + out.password_error = "Pad introuvable !
Cette erreur peut provenir de deux facteurs. Soit le mot de passe est faux, soit le pad a été supprimé du serveur."; + out.password_placeholder = "Tapez le mot de passe ici..."; + out.password_submit = "Valider"; + // New share modal out.share_linkCategory = "Partage"; out.share_linkAccess = "Droits d'accès"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index ddaa1158f..4c3720151 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -1144,6 +1144,12 @@ define(function () { out.creation_newPadModalDescriptionAdvanced = "You can check the box (or press Space to change its value) if you want to display the pad creation screen (for owned pads, expiring pads, etc.)."; out.creation_newPadModalAdvanced = "Display the pad creation screen"; + // Password prompt on the loadind screen + out.password_info = "The pad you're tyring to open is protected with a password. Enter the correct password to access its content."; + out.password_error = "Pad not found!
This error can be caused by two factors: either the password in invalid, or the pad has been deleted from the server."; + out.password_placeholder = "Type the password here..."; + out.password_submit = "Submit"; + // New share modal out.share_linkCategory = "Share link"; out.share_linkAccess = "Access rights"; diff --git a/www/code/inner.js b/www/code/inner.js index c2e5851bf..0a6b11abc 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -334,6 +334,7 @@ define([ //var cursor = editor.getCursor(); //var cleanName = data.name.replace(/[\[\]]/g, ''); //var text = '!['+cleanName+']('+data.url+')'; + // PASSWORD_FILES var parsed = Hash.parsePadUrl(data.url); var hexFileName = Util.base64ToHex(parsed.hashData.channel); var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 852260acc..48d0e622f 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -96,6 +96,7 @@ define([ Hash.createRandomHash = function (type, password) { var cryptor = Crypto.createEditCryptor2(void 0, void 0, password); + console.log(cryptor); return getEditHashFromKeys({ password: Boolean(password), version: 2, @@ -262,6 +263,7 @@ Version 1 var getRelativeHref = Hash.getRelativeHref = function (href) { if (!href) { return; } if (href.indexOf('#') === -1) { return; } + // Password not needed to get the type or the hash var parsed = parsePadUrl(href); return '/' + parsed.type + '/#' + parsed.hash; }; @@ -287,14 +289,15 @@ Version 1 var hash; if (secretHash) { if (!type) { throw new Error("getSecrets with a hash requires a type parameter"); } + // Password not needed here, we only use the hash key parsed = parseTypeHash(type, secretHash); hash = secretHash; } else { + // Password not needed here, we only use the hash key var pHref = parsePadUrl(window.location.href); parsed = pHref.hashData; hash = pHref.hash; } - //var parsed = parsePadUrl(window.location.href); //var hash = secretHash || window.location.hash.slice(1); if (hash.length === 0) { generate(); @@ -337,10 +340,10 @@ Version 1 // New hash secret.version = 2; secret.type = type; - secret.password = Boolean(password); + secret.password = password; if (parsed.type === "pad") { if (parsed.mode === 'edit') { - secret.keys = Crypto.createEditCryptor2(parsed.key); + secret.keys = Crypto.createEditCryptor2(parsed.key, void 0, password); secret.channel = base64ToHex(secret.keys.chanId); secret.key = secret.keys.editKeyStr; if (secret.channel.length !== 32 || secret.key.length !== 24) { @@ -348,7 +351,7 @@ Version 1 } } else if (parsed.mode === 'view') { - secret.keys = Crypto.createViewCryptor2(parsed.key); + secret.keys = Crypto.createViewCryptor2(parsed.key, password); secret.channel = base64ToHex(secret.keys.chanId); if (secret.channel.length !== 32) { throw new Error("The channel key is invalid"); @@ -388,14 +391,14 @@ Version 1 }; // STORAGE - Hash.findWeaker = function (href, recents) { + Hash.findWeaker = function (href, recents, password) { var rHref = href || getRelativeHref(window.location.href); - var parsed = parsePadUrl(rHref); + var parsed = parsePadUrl(rHref, password); if (!parsed.hash) { return false; } var weaker; Object.keys(recents).some(function (id) { var pad = recents[id]; - var p = parsePadUrl(pad.href); + var p = parsePadUrl(pad.href, pad.password); if (p.type !== parsed.type) { return; } // Not the same type if (p.hash === parsed.hash) { return; } // Same hash, not stronger var pHash = p.hashData; @@ -408,23 +411,23 @@ Version 1 if (pHash.version !== parsedHash.version) { return; } if (pHash.channel !== parsedHash.channel) { return; } if (pHash.mode === 'view' && parsedHash.mode === 'edit') { - weaker = pad.href; + weaker = pad; return true; } return; }); return weaker; }; - var findStronger = Hash.findStronger = function (href, recents) { + var findStronger = Hash.findStronger = function (href, recents, password) { var rHref = href || getRelativeHref(window.location.href); - var parsed = parsePadUrl(rHref); + var parsed = parsePadUrl(rHref, password); if (!parsed.hash) { return false; } // We can't have a stronger hash if we're already in edit mode if (parsed.hashData && parsed.hashData.mode === 'edit') { return; } var stronger; Object.keys(recents).some(function (id) { var pad = recents[id]; - var p = parsePadUrl(pad.href); + var p = parsePadUrl(pad.href, pad.password); if (p.type !== parsed.type) { return; } // Not the same type if (p.hash === parsed.hash) { return; } // Same hash, not stronger var pHash = p.hashData; @@ -437,19 +440,16 @@ Version 1 if (pHash.version !== parsedHash.version) { return; } if (pHash.channel !== parsedHash.channel) { return; } if (pHash.mode === 'edit' && parsedHash.mode === 'view') { - stronger = pad.href; + stronger = pad; return true; } return; }); return stronger; }; - Hash.isNotStrongestStored = function (href, recents) { - return findStronger(href, recents); - }; - Hash.hrefToHexChannelId = function (href) { - var parsed = Hash.parsePadUrl(href); + Hash.hrefToHexChannelId = function (href, password) { + var parsed = Hash.parsePadUrl(href, password); if (!parsed || !parsed.hash) { return; } parsed = parsed.hashData; diff --git a/www/common/common-interface.js b/www/common/common-interface.js index ea2773e37..6f4850010 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -594,7 +594,12 @@ define([ $('.cp-loading-spinner-container').hide(); $('#cp-loading-tip').remove(); if (transparent) { $('#' + LOADING).css('opacity', 0.9); } - $('#' + LOADING).find('p').show().html(error || Messages.error); + var $error = $('#' + LOADING).find('p').show(); + if (error instanceof Element) { + $error.html('').append(error); + } else { + $error.html(error || Messages.error); + } if (exitable) { $(window).focus(); $(window).keydown(function (e) { @@ -624,6 +629,7 @@ define([ var type = data.type; if (!href && !type) { return $icon; } + // Password not needed to get the type if (!type) { type = Hash.parsePadUrl(href).type; } $icon = UI.getIcon(type); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 2d09b2abe..9cfe64076 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -67,7 +67,7 @@ define([ common.getPadAttribute('href', waitFor(function (err, val) { var base = common.getMetadataMgr().getPrivateData().origin; - var parsed = Hash.parsePadUrl(val); + var parsed = Hash.parsePadUrl(val, data.password); if (parsed.hashData.mode === "view") { data.roHref = base + val; return; @@ -285,6 +285,7 @@ define([ var hash = (edit && hashes.editHash) ? hashes.editHash : hashes.viewHash; var href = origin + pathname + '#' + hash; + // Password not needed here since we don't access hashData var parsed = Hash.parsePadUrl(href); return origin + parsed.getUrl({embed: embed, present: present}); }; @@ -322,6 +323,7 @@ define([ var getEmbedValue = function () { var hash = hashes.viewHash || hashes.editHash; var href = origin + pathname + '#' + hash; + // Password not needed here since we don't access hashData var parsed = Hash.parsePadUrl(href); var url = origin + parsed.getUrl({embed: true, present: true}); return ''; @@ -930,14 +932,14 @@ define([ }; }; + var setHTML = function (e, html) { + e.innerHTML = html; + return e; + }; + UIElements.createHelpMenu = function (common, categories) { var type = common.getMetadataMgr().getMetadata().type || 'pad'; - var setHTML = function (e, html) { - e.innerHTML = html; - return e; - }; - var elements = []; if (Messages.help && Messages.help.generic) { Object.keys(Messages.help.generic).forEach(function (el) { @@ -1911,6 +1913,17 @@ define([ createHelper('/faq.html#keywords-expiring', Messages.creation_expire2), ]); + // Password + var password = h('div.cp-creation-password', [ + UI.createCheckbox('cp-creation-password', 'TODO Add a password', false), //XXX + h('span.cp-creation-password-picker.cp-creation-slider', [ + h('input#cp-creation-password-val', { + type: "text" // TODO type password with click to show + }), + ]), + createHelper('#', "TODO: password protection adds another layer of security ........") // TODO + ]); + var right = h('span.fa.fa-chevron-right.cp-creation-template-more'); var left = h('span.fa.fa-chevron-left.cp-creation-template-more'); var templates = h('div.cp-creation-template', [ @@ -1936,6 +1949,7 @@ define([ $(h('div#cp-creation-form', [ owned, expire, + password, settings, templates, createDiv @@ -2046,6 +2060,19 @@ define([ $creation.focus(); }); + // Display expiration form when checkbox checked + $creation.find('#cp-creation-password').on('change', function () { + if ($(this).is(':checked')) { + $creation.find('.cp-creation-password-picker:not(.active)').addClass('active'); + $creation.find('.cp-creation-password:not(.active)').addClass('active'); + $creation.find('#cp-creation-password-val').focus(); + return; + } + $creation.find('.cp-creation-password-picker').removeClass('active'); + $creation.find('.cp-creation-password').removeClass('active'); + $creation.focus(); + }); + // Display settings help when checkbox checked $creation.find('#cp-creation-remember').on('change', function () { if ($(this).is(':checked')) { @@ -2094,12 +2121,16 @@ define([ } expireVal = ($('#cp-creation-expire-val').val() || 0) * unit; } + // Password + var passwordVal = $('#cp-creation-password').is(':checked') ? + $('#cp-creation-password-val').val() : undefined; var $template = $creation.find('.cp-creation-template-selected'); var templateId = $template.data('id') || undefined; return { owned: ownedVal, + password: passwordVal, expire: expireVal, templateId: templateId }; @@ -2169,5 +2200,43 @@ define([ (cb || function () {})(); }; + UIElements.displayPasswordPrompt = function (common, isError) { + var error; + if (isError) { error = setHTML(h('p.cp-password-error'), Messages.password_error); } + var info = h('p.cp-password-info', Messages.password_info); + var input = h('input', { + type: "password", + placeholder: Messages.password_placeholder + }); + var button = h('button', Messages.password_submit); + + var submit = function () { + var value = $(input).val(); + UI.addLoadingScreen(); + common.getSframeChannel().query('Q_PAD_PASSWORD_VALUE', value, function (err, data) { + if (!data) { + UIElements.displayPasswordPrompt(common, true); + } + }); + }; + $(input).on('keydown', function (e) { + if (e.which === 13) { submit(); } + }) + $(button).on('click', function () { + submit(); + }) + + + var block = h('div#cp-loading-password-prompt', [ + error, + info, + h('p.cp-password-form', [ + input, + button + ]) + ]); + UI.errorLoadingScreen(block); + }; + return UIElements; }); diff --git a/www/common/cryptget.js b/www/common/cryptget.js index d5cb3edc1..623f5946e 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -43,7 +43,6 @@ define([ Object.keys(b).forEach(function (k) { a[k] = b[k]; }); }; - // XXX make sure we pass the password here in opt var get = function (hash, cb, opt) { if (typeof(cb) !== 'function') { throw new Error('Cryptget expects a callback'); @@ -63,7 +62,6 @@ define([ Session.realtime = CPNetflux.start(config); }; - // XXX make sure we pass the password here in opt var put = function (hash, doc, cb, opt) { if (typeof(cb) !== 'function') { throw new Error('Cryptput expects a callback'); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index e473b397c..dbc69198b 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -385,6 +385,7 @@ define([ if (!type) { return void cb(null, obj); } var templates = obj.filter(function (f) { + // Password not needed here since we don't access hashData var parsed = Hash.parsePadUrl(f.href); return parsed.type === type; }); @@ -393,10 +394,13 @@ define([ }; common.saveAsTemplate = function (Cryptput, data, cb) { + // Password not needed here since we don't access hashData var p = Hash.parsePadUrl(window.location.href); if (!p.type) { return; } + // XXX PPP var hash = Hash.createRandomHash(p.type); var href = '/' + p.type + '/#' + hash; + // XXX PPP Cryptput(hash, data.toSave, function (e) { if (e) { throw new Error(e); } postMessage("ADD_PAD", { @@ -419,16 +423,35 @@ define([ }); }; - common.useTemplate = function (href, Crypt, cb, opts) { + common.useTemplate = function (href, Crypt, cb, optsPut) { // opts is used to overrides options for chainpad-netflux in cryptput // it allows us to add owners and expiration time if it is a new file + + // Password not needed here, we only need the hash and to know if + // we need to get the password var parsed = Hash.parsePadUrl(href); + var parsed2 = Hash.parsePadUrl(window.location.href); if(!parsed) { throw new Error("Cannot get template hash"); } postMessage("INCREMENT_TEMPLATE_USE", href); - Crypt.get(parsed.hash, function (err, val) { - if (err) { throw new Error(err); } - var p = Hash.parsePadUrl(window.location.href); - Crypt.put(p.hash, val, cb, opts); + + optsPut = optsPut || {}; + var optsGet = {}; + Nthen(function (waitFor) { + if (parsed.hashData && parsed.hashData.password) { + common.getPadAttribute('password', waitFor(function (err, password) { + optsGet.password = password; + }), href); + } + if (parsed2.hashData && parsed2.hashData.password) { + common.getPadAttribute('password', waitFor(function (err, password) { + optsPut.password = password; + })); + } + }).nThen(function (waitFor) { + Crypt.get(parsed.hash, function (err, val) { + if (err) { throw new Error(err); } + Crypt.put(parsed2.hash, val, cb, optsPut); + }, optsGet); }); }; @@ -441,6 +464,8 @@ define([ // When opening a new pad or renaming it, store the new title common.setPadTitle = function (title, padHref, path, cb) { var href = padHref || window.location.href; + + // Password not needed here since we don't access hashData var parsed = Hash.parsePadUrl(href); if (!parsed.hash) { return; } href = parsed.getUrl({present: parsed.present}); @@ -477,6 +502,9 @@ define([ common.setInitialPath = function (path) { postMessage("SET_INITIAL_PATH", path); }; + common.setNewPadPassword = function (password) { + postMessage("SET_NEW_PAD_PASSWORD", password); + }; // Messaging (manage friends from the userlist) common.inviteFromUserlist = function (netfluxId, cb) { @@ -559,12 +587,13 @@ define([ hashes = Hash.getHashes(secret); return void cb(null, hashes); } + // Password not needed here since only want the type var parsed = Hash.parsePadUrl(window.location.href); if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); } if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); } hashes = Hash.getHashes(secret); - if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) { + if (secret.version === 0) { // It means we're using an old hash hashes.editHash = window.location.hash.slice(1); return void cb(null, hashes); @@ -576,7 +605,8 @@ define([ } postMessage("GET_STRONGER_HASH", { - href: window.location.href + href: window.location.href, + password: secret.password }, function (hash) { if (hash) { hashes.editHash = hash; } cb(null, hashes); diff --git a/www/common/mergeDrive.js b/www/common/mergeDrive.js index c51428b2a..a20cd798b 100644 --- a/www/common/mergeDrive.js +++ b/www/common/mergeDrive.js @@ -122,21 +122,15 @@ define([ // Do not migrate a pad if we already have it, it would create a duplicate in the drive if (newHrefs.indexOf(href) !== -1) { return; } // If we have a stronger version, do not add the current href - if (Hash.findStronger(href, newRecentPads)) { return; } + if (Hash.findStronger(href, newRecentPads, oldRecentPads[id].password)) { return; } // If we have a weaker version, replace the href by the new one // NOTE: if that weaker version is in the trash, the strong one will be put in unsorted - var weaker = Hash.findWeaker(href, newRecentPads); + var weaker = Hash.findWeaker(href, newRecentPads, oldRecentPads[id].password); if (weaker) { // Update RECENTPADS - newRecentPads.some(function (pad) { - if (pad.href === weaker) { - pad.href = href; - return true; - } - return; - }); + weaker.href = href; // Update the file in the drive - newFo.replace(weaker, href); + newFo.replace(weaker.href, href); return; } // Here it means we have a new href, so we should add it to the drive at its old location diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 676277475..2ce6d58f2 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -416,6 +416,7 @@ define([ var pad = makePad(data.href, data.title); if (data.owners) { pad.owners = data.owners; } if (data.expire) { pad.expire = data.expire; } + if (data.password) { pad.password = data.password; } store.userObject.pushData(pad, function (e, id) { if (e) { return void cb({error: "Error while adding a template:"+ e}); } var path = data.path || ['root']; @@ -758,8 +759,10 @@ define([ title: title, owners: owners, expire: expire, + password: store.data && store.data.newPadPassword, path: data.path || (store.data && store.data.initialPath) }, cb); + delete store.data.newPadPassword; return; } onSync(cb); @@ -802,6 +805,11 @@ define([ if (!store.data) { return; } store.data.initialPath = path; }; + Store.setNewPadPassword = function (password) { + if (!store.data) { return; } + store.data.newPadPassword = password; + }; + // Messaging (manage friends from the userlist) var getMessagingCfg = function () { @@ -833,9 +841,9 @@ define([ var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {}; // If we have a stronger version in drive, add it and add a redirect button - var stronger = Hash.findStronger(data.href, allPads); + var stronger = Hash.findStronger(data.href, allPads, data.password); if (stronger) { - var parsed2 = Hash.parsePadUrl(stronger); + var parsed2 = Hash.parsePadUrl(stronger.href, stronger.password); return void cb(parsed2.hash); } cb(); diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 1713d7a2a..e95f6e227 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -123,6 +123,9 @@ define([ case 'SET_INITIAL_PATH': { Store.setInitialPath(data); break; } + case 'SET_NEW_PAD_PASSWORD': { + Store.setNewPadPassword(data); break; + } case 'GET_STRONGER_HASH': { Store.getStrongerHash(data, cb); break; } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index e6543bb30..990208ee3 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -24,6 +24,7 @@ define([ var Utils = {}; var AppConfig; var Test; + var password; nThen(function (waitFor) { // Load #2, the loading screen is up so grab whatever you need... @@ -121,26 +122,53 @@ define([ }); })); } else { - var parsedType = Utils.Hash.parsePadUrl(window.location.href).type; - // XXX prompt the password here if we have a hash containing /p/ - // OR get it from the pad attributes - secret = Utils.Hash.getSecrets(parsedType); - - // TODO: New hashes V2 already contain a channel ID so we can probably remove the following lines - //if (!secret.channel) { - // New pad: create a new random channel id - //secret.channel = Utils.Hash.createChannelId(); - //} - - Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); + var parsed = Utils.Hash.parsePadUrl(window.location.href); + var todo = function () { + secret = Utils.Hash.getSecrets(parsed.type, void 0, password); + Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); + }; + + // Prompt the password here if we have a hash containing /p/ + // or get it from the pad attributes + var needPassword = parsed.hashData && parsed.hashData.password; + if (needPassword) { + Cryptpad.getPadAttribute('password', waitFor(function (err, val) { + if (val) { + // We already know the password, use it! + password = val; + todo(); + } else { + // Ask for the password and check if the pad exists + // If the pad doesn't exist, it means the password is oncorrect + // or the pad has been deleted + var correctPassword = waitFor(); + sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) { + password = data; + Cryptpad.isNewChannel(window.location.href, password, function (e, isNew) { + if (Boolean(isNew)) { + // Ask again in the inner iframe + // We should receive a new Q_PAD_PASSWORD_VALUE + cb(false); + } else { + todo(); + correctPassword(); + cb(true); + } + }); + }); + sframeChan.event("EV_PAD_PASSWORD"); + } + })); + return; + } + // If no password, continue... + todo(); } }).nThen(function (waitFor) { // Check if the pad exists on server if (!window.location.hash) { isNewFile = true; return; } if (realtime) { - // XXX get password - var password; Cryptpad.isNewChannel(window.location.href, password, waitFor(function (e, isNew) { if (e) { return console.error(e); } isNewFile = Boolean(isNew); @@ -679,11 +707,12 @@ define([ sframeChan.on('Q_CREATE_PAD', function (data, cb) { if (!isNewFile || rtStarted) { return; } // Create a new hash - // XXX add password here var password = data.password; var newHash = Utils.Hash.createRandomHash(parsed.type, password); secret = Utils.Hash.getSecrets(parsed.type, newHash, password); + Cryptpad.setNewPadPassword(password); + // Update the hash in the address bar var ohc = window.onhashchange; window.onhashchange = function () {}; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index a56afa382..444ee40e7 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -198,6 +198,7 @@ define([ ctx.sframeChan.query("Q_CREATE_PAD", { owned: cfg.owned, expire: cfg.expire, + password: cfg.password, template: cfg.template, templateId: cfg.templateId }, cb); @@ -379,6 +380,7 @@ define([ Object.freeze(funcs); return { create: function (cb) { + console.log('create'); if (window.CryptPad_sframe_common) { throw new Error("Sframe-common should only be created once"); } @@ -429,6 +431,14 @@ define([ UI.log(data.logText); }); + ctx.sframeChan.on("EV_PAD_PASSWORD", function (data) { + UIElements.displayPasswordPrompt(funcs); + /*UI.prompt("Password?", "", function (val) { + ctx.sframeChan.event("EV_PAD_PASSWORD_VALUE", val); + }); + $('div.alertify').last().css("z-index", Number.MAX_SAFE_INTEGER);*/ + }); + ctx.metadataMgr.onReady(waitFor()); }).nThen(function () { try { diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js index 8142694ce..970ec5524 100644 --- a/www/common/sframe-protocol.js +++ b/www/common/sframe-protocol.js @@ -230,4 +230,8 @@ define({ // Critical error outside the iframe during loading screen 'EV_LOADING_ERROR': true, + + // Ask for the pad password when a pad is protected + 'EV_PAD_PASSWORD': true, + 'Q_PAD_PASSWORD_VALUE': true, }); diff --git a/www/poll/inner.js b/www/poll/inner.js index fabcad7e4..ad0ea5ada 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -2,7 +2,6 @@ define([ 'jquery', '/common/toolbar3.js', '/common/common-util.js', - '/common/cryptget.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', '/common/common-realtime.js', @@ -32,7 +31,6 @@ define([ $, Toolbar, Util, - Cryptget, nThen, SFCommon, CommonRealtime,