diff --git a/www/common/common-hash.js b/www/common/common-hash.js index c59f5babe..b9cdfa699 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -215,6 +215,17 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) }); return k ? Crypto.b64AddSlashes(k) : ''; }; + var getAuditorKey = function (hashArr) { + var k; + // Check if we have a ownerKey for this pad + hashArr.some(function (data) { + if (/^auditor=/.test(data)) { + k = data.slice(8); + return true; + } + }); + return k ? Crypto.b64AddSlashes(k) : ''; + }; var getOwnerKey = function (hashArr) { var k; // Check if we have a ownerKey for this pad @@ -237,6 +248,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) parsed.present = options.indexOf('present') !== -1; parsed.embed = options.indexOf('embed') !== -1; parsed.versionHash = getVersionHash(options); + parsed.auditorKey = getAuditorKey(options); parsed.newPadOpts = getNewPadOpts(options); parsed.loginOpts = getLoginOpts(options); parsed.ownerKey = getOwnerKey(options); @@ -278,6 +290,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) present: parsed.present, ownerKey: parsed.ownerKey, versionHash: parsed.versionHash, + auditorKey: parsed.auditorKey, newPadOpts: parsed.newPadOpts, loginOpts: parsed.loginOpts, password: parsed.password @@ -304,6 +317,10 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) if (versionHash) { hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/'; } + var auditorKey = typeof(opts.auditorKey) !== "undefined" ? opts.auditorKey : parsed.auditorKey; + if (auditorKey) { + hash += 'auditor=' + Crypto.b64RemoveSlashes(auditorKey) + '/'; + } if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } return hash; diff --git a/www/common/inner/share.js b/www/common/inner/share.js index f2eb2e953..5db88b672 100644 --- a/www/common/inner/share.js +++ b/www/common/inner/share.js @@ -494,7 +494,23 @@ define([ var parsed = Hash.parsePadUrl(pathname); var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1; var versionHash = hashes.viewHash && opts.versionHash; - var canBAR = parsed.type !== 'drive' && !versionHash; + var isForm = parsed.type === "form"; // && opts.auditorHash; + var canBAR = parsed.type !== 'drive' && !versionHash && !isForm; + + var labelEdit = Messages.share_linkEdit; + var labelView = Messages.share_linkView; + + var auditor; + if (isForm) { + Messages.share_formEdit = "Author"; // XXX + Messages.share_formView = "Participant"; // XXX + Messages.share_formAuditor = "Auditor"; // XXX + labelEdit = Messages.share_formEdit; + labelView = Messages.share_formView; + auditor = UI.createRadio('accessRights', 'cp-share-form', Messages.share_formAuditor, false, { + mark: {tabindex:1}, + }); + } var burnAfterReading = (hashes.viewHash && canBAR) ? UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, { @@ -505,12 +521,13 @@ define([ h('label', Messages.share_linkAccess), h('div.radio-group',[ UI.createRadio('accessRights', 'cp-share-editable-false', - Messages.share_linkView, true, { mark: {tabindex:1} }), + labelView, true, { mark: {tabindex:1} }), canPresent ? UI.createRadio('accessRights', 'cp-share-present', Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined, UI.createRadio('accessRights', 'cp-share-editable-true', - Messages.share_linkEdit, false, { mark: {tabindex:1} })]), - burnAfterReading + labelEdit, false, { mark: {tabindex:1} }), + auditor]), + burnAfterReading, ]); // Burn after reading @@ -553,6 +570,7 @@ define([ var embed = val.embed; var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present')); var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar')); + var formAuditor = Util.isChecked($rights.find('#cp-share-form')); if (versionHash) { edit = false; present = false; @@ -569,6 +587,9 @@ define([ } var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash; + if (formAuditor && opts.auditorHash) { + hash = opts.auditorHash; + } var href = burnAfterReading ? opts.burnAfterReadingUrl : (origin + pathname + '#' + hash); var parsed = Hash.parsePadUrl(href); @@ -594,6 +615,9 @@ define([ $rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true); $rights.find('#cp-share-editable-true').attr('checked', true); } + if (isForm && !opts.auditorHash) { + $rights.find('#cp-share-form').removeAttr('checked').attr('disabled', true); + } var getLink = function () { return $rights.parent().find('#cp-share-link-preview'); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 119d7fb3d..26ff870f6 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -553,11 +553,13 @@ MessengerUI, Messages, Pages) { if (toolbar.isDeleted) { return void UI.warn(Messages.deletedFromServer); } + var privateData = config.metadataMgr.getPrivateData(); var title = (config.title && config.title.getTitle && config.title.getTitle()) || (config.title && config.title.defaultName) || ""; Common.getSframeChannel().event('EV_SHARE_OPEN', { - title: title + title: title, + auditorHash: privateData.form_auditorHash }); }); diff --git a/www/form/inner.js b/www/form/inner.js index d82aeae90..f37b3f3e4 100644 --- a/www/form/inner.js +++ b/www/form/inner.js @@ -689,6 +689,20 @@ define([ // XXX fetch answers and // * viewers ==> check if you've already answered and show form (new or edit) // * editors ==> show schema and warn users if existing questions already have answers + + if (priv.form_auditorKey) { + sframeChan.query("Q_FORM_FETCH_ANSWERS", { + channel: content.answers.channel, + validateKey: content.answers.validateKey, + publicKey: content.answers.publicKey, + privateKey: priv.form_auditorKey + }, function (err, obj) { + $body.addClass('cp-app-form-results'); + renderResults(content, obj); + }); + return; + } + if (APP.isEditor) { sframeChan.query("Q_FORM_FETCH_ANSWERS", { channel: content.answers.channel, diff --git a/www/form/main.js b/www/form/main.js index 32810bd1c..4ebe99295 100644 --- a/www/form/main.js +++ b/www/form/main.js @@ -19,6 +19,13 @@ define([ var privateKey, publicKey; var addData = function (meta, CryptPad, user, Utils) { var keys = Utils.secret && Utils.secret.keys; + + var parsed = Utils.Hash.parseTypeHash('pad', hash.slice(1)); + if (parsed.auditorKey) { + meta.form_auditorKey = parsed.auditorKey; + meta.form_auditorHash = hash; + } + var secondary = keys && keys.secondaryKey; if (!secondary) { return; } var curvePair = Nacl.box.keyPair.fromSecretKey(Nacl.util.decodeUTF8(secondary).slice(0,32)); @@ -27,10 +34,19 @@ define([ publicKey = meta.form_public = Nacl.util.encodeBase64(curvePair.publicKey); privateKey = meta.form_private = Nacl.util.encodeBase64(curvePair.secretKey); + + var auditorHash = Utils.Hash.getViewHashFromKeys({ + version: 1, + channel: Utils.secret.channel, + keys: { viewKeyStr: Nacl.util.encodeBase64(keys.cryptKey) } + }); + var parsed = Utils.Hash.parseTypeHash('pad', auditorHash); + meta.form_auditorHash = parsed.getHash({auditorKey: privateKey}); + }; var addRpc = function (sframeChan, Cryptpad, Utils) { sframeChan.on('Q_FORM_FETCH_ANSWERS', function (data, cb) { - var myKeys; + var myKeys = {}; var CPNetflux; var network; nThen(function (w) { @@ -48,7 +64,6 @@ define([ return true; } }); - console.error(myKeys); })); Cryptpad.makeNetwork(w(function (err, nw) { network = nw; diff --git a/www/secureiframe/inner.js b/www/secureiframe/inner.js index 1c5af4a57..4226c7f82 100644 --- a/www/secureiframe/inner.js +++ b/www/secureiframe/inner.js @@ -58,6 +58,7 @@ define([ hashes: data.hashes || priv.hashes, common: common, title: data.title, + auditorHash: data.auditorHash, versionHash: data.versionHash, friends: friends, onClose: function () {