Revocation: add/update/revoke access (clientside)

This commit is contained in:
yflory 2023-04-06 18:53:41 +02:00
parent 935cb34f4b
commit aa9d4e950b
8 changed files with 430 additions and 189 deletions

View File

@ -72,7 +72,7 @@ module.exports.create = function (Env, cb) {
if (metadata.access) {
if (!signature) { return void ('ERESTRICTED'); }
let c = Revocable.checkRead(channelName, userId, signature, metadata);
return void cb(!c ? 'EFORBIDDEN' : undefined);
return void cb(!c ? 'ERESTRICTED' : undefined);
}
if (!metadata.restricted) {

View File

@ -352,6 +352,7 @@ define([
var revocation = common.makeUniversal('revocation');
var updateAccess = function () {};
var addAccess = function () {};
var TYPES = {
user: { icon: '.fa.fa-user', order: 1 },
@ -390,9 +391,16 @@ define([
buttonCls: 'btn btn-secondary'
};
var select = UIElements.createDropdown(dropdownConfig);
select.setValue(value);
select.setValue(current);
return select;
};
var getRights = function (dd, $d) {
var r = dd.getValue() || 'r';
var d = Util.isChecked($d);
var rights = r === 'm' ? 'rwm' : (r === 'w' ? 'rw' : 'r');
if (d) { rights += 'd'; }
return rights;
};
var renderAccess = function (edPublic, accessData, editable, maxR, renderedAs) {
var type = accessData.notes.type
var icon = TYPES[type].icon;
@ -407,32 +415,29 @@ define([
h('span', 'REVOKE') // XXX
]);
if (!editable) { $(revoke).attr('disabled', 'disabled'); }
var getRights = function () {};
else {
$(revoke).click(function () {
UI.confirm(Messages.areYouSure, function (yes) { // XXX REVOCATION message
if (!yes) { return; }
updateAccess(edPublic, false, renderedAs.key);
});
});
}
var $d = $(canDestroy).find('input');
if (editable && maxR === 'm') {
$d.on('change', function () {
updateAccess(edPublic, getRights(), renderedAs.key);
updateAccess(edPublic, getRights(dd, $d), renderedAs.key);
});
}
var dd = makeDD(rights, editable, maxR);
if (dd.onChange) {
dd.onChange.reg(function () {
console.warn('ici');
console.error(getRights());
updateAccess(edPublic, getRights(), renderedAs.key);
updateAccess(edPublic, getRights(dd, $d), renderedAs.key);
});
}
getRights = function () {
var r = dd.getValue() || 'r';
var d = Util.isChecked($d);
var rights = r === 'm' ? 'rwm' : (r === 'w' ? 'rw' : 'r');
if (d) { rights += 'd'; }
return rights;
};
return h('div.cp-share-access', {
order: TYPES[type].order
@ -446,18 +451,72 @@ define([
]);
};
var addAccessButton = function (maxR, renderedAs) {
// new form
var input = h('input', {placeholder:'Note'}); // XXX
var dd = makeDD('r', true, maxR);
var canDestroy = UI.createCheckbox('cp-share-can-destroy', h('i.fa.fa-trash'), false, {});
var $d = $(canDestroy).find('input');
var saveBtn = h('button.btn.btn-primary', [
h('i.fa.fa-floppy-o'),
h('span', 'SAVE') // XXX
]);
var cancelBtn = h('button.btn.btn-cancel', [
h('i.fa.fa-times')
]);
var temp = h('div.cp-share-access', {order:100, style: 'display:none;'}, [
h('i.fa.fa-plus'),
input,
dd[0],
canDestroy,
saveBtn,
cancelBtn
]);
var $temp = $(temp);
// show form btn
var button = h('button.btn.btn-primary', [
h('i.fa.fa-plus'),
h('span', 'ADD') // XXX
]);
var $b = $(button);
$(saveBtn).click(function () {
var access = getRights(dd, $d);
var note = {
type: 'link',
note: $(input).val()
};
if (false) { note.edPublic = "edpublic"; } // XXX user access
addAccess(access, note, renderedAs.key);
});
$(cancelBtn).click(function () {
$b.show();
$temp.hide();
});
$b.click(function () {
$b.hide();
$temp.css('display', 'flex');
});
$content.append(temp);
$(content).append(h('div', button));
};
var renderAll = function (obj, renderedAs) {
$content.empty();
var list = obj.list;
var myAccess = list[renderedAs.key];
var maxRights = myAccess.rights.includes('m') ? 'm' :
(myAccess.rights.includes('w') ? 'w' : 'r');
Object.keys(list || {}).forEach(function (ed) {
var editable = renderedAs.moderator || renderedAs.key === list[ed].from;
var myAccess = list[renderedAs.key];
var maxRights = myAccess.rights.includes('m') ? 'm' :
(myAccess.rights.includes('w') ? 'w' : 'r');
var a = renderAccess(ed, list[ed], editable, maxRights, renderedAs);
$content.append(a);
});
addAccessButton(maxRights, renderedAs);
};
var renderAs = function (obj) {
$viewAs.empty();
@ -491,6 +550,18 @@ define([
renderAll(obj, myKeys[0]);
};
addAccess = function (rights, note, updateAs) {
revocation.execCommand('ADD_ACCESS', {
type: priv.app,
channel: channel,
rights: rights,
note: note,
from: updateAs
}, function (obj) {
console.warn(obj);
// XXX refresh view
});
};
updateAccess = function (user, rights, updateAs) {
var access = !rights ? false : {
rights: rights
@ -503,7 +574,7 @@ define([
},
from: updateAs
}, function () {
// XXX refresh view
});
};
@ -520,9 +591,6 @@ define([
renderAs(obj);
});
var b = h('button', 'button');
cb(void 0, {
content: content,
buttons: [{

View File

@ -3138,6 +3138,7 @@ define([
// initialize the chat (messenger) and the cursor modules.
loadUniversal(Cursor, 'cursor', function () {});
loadUniversal(Messenger, 'messenger', function () {});
loadUniversal(Revocation, 'revocation', waitFor, clientId);
store.messenger = store.modules['messenger'];
// And now we're ready

View File

@ -659,6 +659,10 @@ proxy.mailboxes = {
sendTo(ctx, type, msg, user, cb);
};
mailbox.sendAs = function (type, msg, to, as, cb) {
Mailbox.sendAs(ctx, as, type, msg, to, cb);
};
mailbox.removeClient = function (clientId) {
removeClient(ctx, clientId);
};

View File

@ -53,10 +53,32 @@ id: {
return pw;
};
var getPadMetadata = function (ctx, channel, cb) {
var getPadMetadata = function (ctx, channel, cb, keys) {
ctx.Store.getPadMetadata(null, {
channel: channel
}, function (md) {
if (!keys || !md || md.error) { return void cb(md); }
var secret = Hash.getRevocableSecret({
channel: '',
viewerSeedStr: keys.doc.viewer,
editorSeedStr: keys.doc.editor,
}, keys.password);
var crypto = Crypto.createEncryptor(secret.keys);
Object.keys(md.access || {}).forEach(function (ed) {
var a = md.access[ed];
if (a.mailbox) {
try {
a.mailbox = crypto.decrypt(a.mailbox, true, true);
} catch (e) { console.error(e); }
}
if (a.notes) { // XXX use moderator keys!
try {
a.notes = JSON.parse(crypto.decrypt(a.notes, true, true));
} catch (e) { console.error(e); }
}
});
cb(md);
});
};
@ -136,7 +158,9 @@ id: {
return content && content.doc && content.doc.channel;
};
var getMailboxParser = function (ctx, onNewKeys) {
var data = {};
var data = {
doc: {}
};
var channel;
var MAILBOX_COMMANDS = {
@ -154,11 +178,23 @@ id: {
if (!i || !data.doc) { return; }
// Trust that all the keys you need are there
// Make sure it was sent by the same moderator than in the moderators log
isValidRotateMessage(ctx, log, msg, channel, waitFor(function (valid, password) {
isValidRotateMessage(ctx, log, msg.content, channel, waitFor(function (valid, password) {
if (!valid) { return; }
data.doc = msg.content
data.password = password;
}));
},
UPDATE: function (msg, log, channel, i, waitFor) {
if (!data || !data.doc) { return; }
var doc = data.doc;
var content = msg.content;
if (content.uid !== doc.uid) { return; } // doesn't match our viewer key version
if (content.editor && !doc.editor) {
doc.editor = content.editor;
}
if (content.moderator && !doc.moderator) {
doc.moderator = content.moderator;
}
}
};
@ -190,9 +226,15 @@ id: {
}).nThen;
});
nn(waitFor(function () {
data.channel = channel;
onNewKeys(Util.clone(data));
var clone = Util.clone(data);
var myAccess = md.access && md.access[data.edPublic];
//if (!myAccess) { return void onNewKeys(false); }
if (!Revocable.isModerator(myAccess)) { delete data.doc.moderator; }
if (!Revocable.isEditor(myAccess)) { delete data.doc.editor; }
onNewKeys(clone);
// XXX once messages handled, emit to client
// XXX password change or keys rotation
// XXX make sure only one notication per pad (in case of multiple mailboxes)
@ -226,7 +268,7 @@ id: {
var box = ctx.mailboxes[secret.channel];
if (box) {
// XXX return existing box
if (box.revoked) { return void cb({error: 'EFORBIDDEN'}); }
if (!box.clients.includes(clientId)) { box.clients.push(clientId); }
return void cb(box.parse.getContent());
}
@ -234,10 +276,10 @@ id: {
box = ctx.mailboxes[secret.channel] = {
messages: [],
clients: [clientId],
secret: secret,
ready: false
};
if (boxData.link || !boxData.store) {
box.origin = 'URL ' + seed; // XXX
} else if (boxData.fId) {
@ -265,6 +307,12 @@ id: {
};
box.parse = getMailboxParser(ctx, function (keys) {
if (keys === false) {
// XXX revoked
// XXX close mailbox, etc.
box.revoked = true;
cb({error: 'EFORBIDDEN'});
}
if (first) { cb(keys); }
first = false;
onNewKeys(keys);
@ -441,34 +489,14 @@ id: {
};
});
var secret = Hash.getRevocableSecret({
channel: '',
viewerSeedStr: best.doc.viewer,
editorSeedStr: best.doc.editor,
}, best.password);
var crypto = Crypto.createEncryptor(secret.keys);
getPadMetadata(ctx, channel, waitFor(function (md) {
if (md && md.error) { return; }
var access = md.access;
Object.keys(access).forEach(function (ed) {
var a = access[ed];
if (a.mailbox) {
try {
a.mailbox = crypto.decrypt(a.mailbox, true, true);
} catch (e) { console.error(e); }
}
if (a.notes) {
try {
a.notes = JSON.parse(crypto.decrypt(a.notes, true, true));
} catch (e) { console.error(e); }
}
});
cb({
list: access,
myKeys: myKeys,
});
}));
}), best);
});
};
@ -505,6 +533,15 @@ updateAccess({
return _box;
};
var sendMailboxMsg = function (ctx, type, content, to, from, cb) {
if (!ctx.store.mailbox) { return void cb({error: 'NOT_READY'}); }
ctx.store.mailbox.sendAs(type, content, to, from, cb);
};
var sendInitMsg = function (ctx, data, cb) {
sendMailboxMsg(ctx, data.type, data.msg, data.user, data.keys, cb)
};
/*
ctx.Store.anonRpcMsg(clientId, {
msg: 'SET_REVOCATION_METADATA',
@ -523,14 +560,36 @@ updateAccess({
}
}, cb);
*/
var sendUpdateMsg = function (ctx, target, newAccess, box, md, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var oldAccess = md.access[target];
var keys = box.parse.getContent();
var toSend = Revocable.getUpgradeMessage(keys.doc, newAccess, oldAccess);
if (!toSend) { return void cb(false); }
toSend.uid = keys.doc.uid;
var sendTo = {
channel: oldAccess.mailbox, // decrypted from getPadMetadata
curvePublic: oldAccess.curvePublic
};
var sendFrom = {
curvePrivate: box.secret.curvePrivate,
curvePublic: box.secret.curvePublic
};
sendMailboxMsg(ctx, 'UPDATE', toSend, sendTo, sendFrom, cb);
};
var updateAccess = function (ctx, obj, clientId, cb) {
var key = obj.from;
var box = findBoxFromKey(ctx, key);
if (!box) { return void cb({error: 'EINVAL'}); }
var edPrivate = box.parse.getContent().edPrivate;
var keys = box.parse.getContent();
getPadMetadata(ctx, obj.channel, function (md) {
if (md && md.error) { return; }
var edPrivate = keys.edPrivate;
var log = Revocable.getSanitizedLog(md);
var last = log[log.length-1];
@ -555,15 +614,6 @@ updateAccess({
var netfluxId = getNetfluxId(ctx);
var sig = Revocable.signLog([JSON.stringify(data), netfluxId], edPrivate);
console.warn({
msg: 'SET_REVOCATION_METADATA',
data: {
data: data,
signature: sig,
key: key
}
});
ctx.Store.anonRpcMsg(clientId, {
msg: 'SET_REVOCATION_METADATA',
data: {
@ -571,14 +621,150 @@ console.warn({
signature: sig,
key: key
}
}, function (obj) {
console.error(obj);
cb(obj);
}, function (res) {
if (obj.add) { return void cb(res); }
sendUpdateMsg(ctx, value.user, newRights, box, md, function (sent) {
if (sent === false) { return; }
console.log('UPDATE message sent');
});
cb(res);
});
}, keys);
};
var addAccess = function (ctx, obj, clientId, cb) {
var key = obj.from;
var box = findBoxFromKey(ctx, key);
if (!box) { return void cb({error: 'EINVAL'}); }
var keys = box.parse.getContent();
var type = obj.type;
var rights = obj.rights;
var note = obj.note;
var doc = Hash.getRevocableSecret({
channel: keys.channel,
viewerSeedStr: keys.doc.viewer,
editorSeedStr: keys.doc.editor,
moderatorSeedStr: keys.doc.moderator
}, keys.password);
var crypto = Crypto.createEncryptor(doc.keys);
var cryptoSym = crypto.encrypt;
var cryptoAsym = crypto.encrypt; // XXX
var mailboxData = Revocable.createMailbox(type, box.secret, keys.doc, rights);
var access = Revocable.createAccess(type, mailboxData, note, cryptoSym, cryptoAsym);
console.error('NEW HASH', Hash.getRevocableHashFromKeys(type, mailboxData.mailbox)); // XXX
sendInitMsg(ctx, mailboxData.initMsg, function () {
updateAccess(ctx, {
add: true,
channel: keys.channel,
value: {
user: mailboxData.edPublic,
access: access
},
from: obj.from,
}, clientId, cb);
});
};
var createPad = function (ctx, obj, clientId, cb) {
var type = obj.type;
var password = obj.password;
var edPublic = obj.edPublic;
// random curve keyPair for the first mailbox
var randomKeys = Hash.getRevocable(type);
var doc = Hash.getRevocableSecret(undefined, password); // generate document keys
var docKeys = {
uid: Util.uid(),
viewer: doc.keys.viewerSeed,
editor: doc.keys.editorSeed,
moderator: doc.keys.moderatorSeed,
moderatorCurve: doc.keys.moderatorCurvePublic,
channel: doc.channel
};
var moderator = Revocable.createMailbox(type, randomKeys, docKeys, 'rwmd');
var editor = Revocable.createMailbox(type, moderator.mailbox, docKeys, 'rw');
console.error('MODERATOR HASH', Hash.getRevocableHashFromKeys(type, moderator.mailbox)); // XXX
var crypto = Crypto.createEncryptor(doc.keys);
var cryptoSym = crypto.encrypt;
var cryptoAsym = crypto.encrypt; // XXX
var access = {};
var modNote = {
type: edPublic ? 'user' : 'link',
note: 'Document creator' // XXX
};
if (edPublic) { modNote.edPublic = edPublic; }
access[moderator.edPublic] = Revocable.createAccess(type, moderator, modNote, cryptoSym, cryptoAsym);
var editNote = {
type: 'link',
note: 'Initial editor access' // XXX
};
access[editor.edPublic] = Revocable.createAccess(type, editor, editNote, cryptoSym, cryptoAsym);
// Get a hash of the cryptKey for initial moderators log
var keyHashStr = Revocable.hashBytes(doc.keys.cryptKey);
var first = Revocable.firstLog(moderator.edPublic);
var prevHash = Revocable.hashMsg(first);
var rotateMsg = Revocable.rotateLog(keyHashStr, doc.keys.validateKey, docKeys.uid, prevHash);
var signature = Revocable.signLog(rotateMsg, moderator.mailbox.edPrivate);
var data = {
// displayed hash
newHash: Hash.getRevocableHashFromKeys(type, editor.mailbox, password),
docKeys: docKeys,
crypto: crypto,
rtConfig: {
creation: {
creatorEdPrivate: doc.keys.creator,
},
authentication: {
edPublic: moderator.mailbox.edPublic,
edPrivate: moderator.mailbox.edPrivate
},
metadata: {
validateKey: (doc.keys && doc.keys.validateKey) || undefined,
access: access,
revocableData: {
creatorKey: moderator.mailbox.curvePublic,
creatorVKey: moderator.mailbox.edPublic,
rotate: {
uid: docKeys.uid,
hash: keyHashStr,
validateKey: doc.keys.validateKey,
signature: signature,
}
}
}
}
};
console.error(doc);
console.error(moderator);
console.error(editor);
console.error(access);
console.error(keyHashStr);
nThen(function (waitFor) {
sendInitMsg(ctx, moderator.initMsg, waitFor());
sendInitMsg(ctx, editor.initMsg, waitFor());
}).nThen(function (waitFor) {
cb(data);
});
};
var leaveChannel = function (ctx, padChan) {
// Leave channel and prevent reconnect when we leave a pad
@ -647,9 +833,15 @@ console.warn({
if (cmd === 'UPDATE_ACCESS') {
return void updateAccess(ctx, data, clientId, cb);
}
if (cmd === 'ADD_ACCESS') {
return void addAccess(ctx, data, clientId, cb);
}
if (cmd === 'LOAD_PAD') {
return void loadPadFromBox(ctx, data, clientId, cb);
}
if (cmd === 'CREATE_PAD') {
return void createPad(ctx, data, clientId, cb);
}
};
return revocation;

View File

@ -4,9 +4,23 @@ var factory = function (Hash, Nacl) {
// Access metadata
Revocable.isModerator = function (access) {
if (typeof(access) === "string") {
return access.includes('m');
}
if (!access || !access.rights) { return false; }
return access.rights.includes('m');
};
Revocable.isEditor = function (access) {
if (typeof(access) === "string") {
return access.includes('w');
}
if (!access || !access.rights) { return false; }
return access.rights.includes('w');
};
Revocable.isValidRights = function (rights) {
if (typeof(rights) !== "string") { return false; }
return /^rw?m?d?$/.test(rights);
};
Revocable.isValidAccessUpdate = function (oldValue, newValue) {
if (oldValue && !newValue) { return true; } // Deletion
if (oldValue) { // Update: I can only change "rights"
@ -141,6 +155,67 @@ var factory = function (Hash, Nacl) {
return result;
};
// Mailbox
var RIGHTS = {
r: 'viewer',
w: 'editor',
m: 'moderator'
};
// Send missing keys to the user
Revocable.getUpgradeMessage = function (docKeys, newAccess, oldAccess) {
if (!newAccess) { return; } // Revoke
if (!oldAccess) { return; } // Create
var oldR = oldAccess.rights || 'r';
var newR = newAccess.rights || 'r';
var toSend = {};
newR.split('').forEach(function (key) {
if (oldR.includes(key)) { return; } // they already have this seed
if (!RIGHTS[key]) { return; } // no destroy seed
var type = RIGHTS[key];
if (!docKeys[type]) { return; } // make sure I know this key
toSend[type] = docKeys[type];
});
if (!Object.keys(toSend).length) { return; }
return toSend;
};
Revocable.createMailbox = function (type, fromKeys, docKeys, rights) {
if (!Revocable.isValidRights(rights)) { return; }
var mailbox = Hash.getRevocable(type);
var clone = JSON.parse(JSON.stringify(docKeys));
if (!Revocable.isModerator(rights)) { delete clone.moderator; }
if (!Revocable.isEditor(rights)) { delete clone.editor; }
var initMsg = {
type: "INIT",
msg: {
doc: clone,
edPublic: mailbox.keys.edPublic,
edPrivate: mailbox.keys.edPrivate,
},
user: mailbox, // send to
keys: fromKeys // send from
};
return {
rights: rights,
initMsg: initMsg,
mailbox: mailbox,
edPublic: mailbox.keys.edPublic
};
};
Revocable.createAccess = function (type, user, notes, encryptSym, encryptAsym, contact) {
if (!Revocable.isValidRights(user.rights)) { return; }
notes.hash = Hash.getRevocableHashFromKeys(type, user.mailbox);
var access = {
rights: user.rights,
mailbox: encryptSym(user.mailbox.channel),
curvePublic: user.mailbox.curvePublic,
notes: encryptAsym(JSON.stringify(notes))
};
if (contact) { access.contact = encryptSym(contact); }
return access;
};
// Authentication

View File

@ -81,6 +81,7 @@ define([
var toolbar;
var state = STATE.DISCONNECTED;
var firstConnection = true;
var restricted = false;
var toolbarContainer = options.toolbarContainer ||
(function () { throw new Error("toolbarContainer must be specified"); }());
@ -200,6 +201,7 @@ define([
break;
}
case STATE.ERROR: {
if (text === 'ERESTRICTED') { restricted = true; }
evStart.reg(function () {
if (text === 'ERESTRICTED') {
toolbar.failed(true);
@ -481,6 +483,7 @@ define([
sframeChan.event("EV_CORRUPTED_CACHE");
};
var onCacheReady = function () {
if (state === STATE.ERROR && restricted) { return; }
stateChange(STATE.INITIALIZING);
toolbar.offline(true);
var newContentStr = cpNfInner.chainpad.getUserDoc();

View File

@ -502,6 +502,9 @@ define([
// XXX fix with password workflow
w.abort();
if (obj && obj.error) {
if (obj.error === 'EFORBIDDEN') {
sframeChan.event("EV_RESTRICTED_ERROR");
}
console.error(obj.error);
return;
}
@ -2113,67 +2116,30 @@ define([
password = data.password;
var newHash = Utils.Hash.createRandomHash(parsed.type, password);
// XXX MAILBOX
var randomKeys = Utils.Hash.getRevocable(parsed.type); // random curve keyPair for the first mailbox
var moderator = Utils.Hash.getRevocable(parsed.type);
var editor = Utils.Hash.getRevocable(parsed.type);
newHash = Utils.Hash.getRevocableHashFromKeys(parsed.type, editor, password); // displayed hash
var doc = Utils.Hash.getRevocableSecret(undefined, password); // generate document keys
var serverCurvePublic;
var rotateUid = Utils.Util.uid();
nThen(function (waitFor) {
var creatorKeys = {
curvePrivate: doc.keys.creator,
curvePublic: doc.keys.channel
};
sendMailboxMsg({
type: "INIT",
msg: {
doc: {
uid: rotateUid,
viewer: doc.keys.viewerSeed,
editor: doc.keys.editorSeed,
moderator: doc.keys.moderatorSeed,
moderatorCurve: doc.keys.moderatorCurvePublic,
channel: doc.channel
},
edPublic: moderator.keys.edPublic,
edPrivate: moderator.keys.edPrivate,
},
user: moderator, // send to
keys: randomKeys // send from
}, waitFor());
sendMailboxMsg({
type: "INIT",
msg: {
doc: {
uid: rotateUid,
viewer: doc.keys.viewerSeed,
editor: doc.keys.editorSeed,
moderatorCurve: doc.keys.moderatorCurvePublic,
channel: doc.channel
},
edPublic: editor.keys.edPublic,
edPrivate: editor.keys.edPrivate,
},
user: editor, // send to
keys: moderator // send from
}, waitFor());
require([
'/api/broadcast?'+ (+new Date()),
], waitFor(function (Broadcast) {
serverCurvePublic = Broadcast.curvePublic;
}));
}).nThen(function (waitFor) {
secret = Utils.secret = doc;
var crypto = Utils.crypto = Utils.Crypto.createEncryptor(Utils.secret.keys);
Cryptpad.universal.execCommand({
type: 'revocation',
data: {
cmd: 'CREATE_PAD',
data: {
type: parsed.type,
password: password,
edPublic: edPublic
}
}
}, function (data) {
var docKeys = data.docKeys;
secret = Utils.secret = Utils.Hash.getRevocableSecret({
channel: docKeys.channel,
viewerSeedStr: docKeys.viewer,
editorSeedStr: docKeys.editor,
}, password);
var crypto = Utils.crypto = Crypto.createEncryptor(secret.keys);
// Update the hash in the address bar
currentPad.hash = newHash;
currentPad.href = '/' + parsed.type + '/#' + newHash;
Cryptpad.setTabHash(newHash);
currentPad.hash = data.newHash;
currentPad.href = '/' + parsed.type + '/#' + data.newHash;
Cryptpad.setTabHash(data.newHash);
parsed = Utils.Hash.parsePadUrl(currentPad.href);
defaultTitle = Utils.UserObject.getDefaultName(parsed);
@ -2181,72 +2147,9 @@ define([
readOnly = false;
updateMeta();
// Create initial accesses
var access = {};
var modData = curvePublic ? {
value: curvePublic,
type: 'user',
note: 'Document creator' // XXX
} : {
value: Utils.Hash.getRevocableHashFromKeys(parsed.type, moderator),
type: 'link',
note: 'Initial moderator access' // XXX
};
console.error('MODERATOR HASH', Utils.Hash.getRevocableHashFromKeys(parsed.type, moderator));
access[moderator.edPublic] = {
rights: 'rwmd',
mailbox: crypto.encrypt(moderator.channel),
curvePublic: moderator.curvePublic,
//contact: crypto.encrypt(), // XXX my contact mailbox
notes: crypto.encrypt(JSON.stringify(modData)),
};
access[editor.edPublic] = {
rights: 'rw',
mailbox: crypto.encrypt(editor.channel),
curvePublic: editor.curvePublic,
notes: crypto.encrypt(JSON.stringify({
value: Utils.Hash.getRevocableHashFromKeys(parsed.type, editor),
type: 'link',
note: 'Initial editor access' // XXX
}))
};
if (data.team) { Cryptpad.initialTeam = data.team.id; }
// Get a hash of the cryptKeyfirst initial moderators log
var keyHashStr = Revocable.hashBytes(doc.keys.cryptKey);
var first = Revocable.firstLog(moderator.keys.edPublic);
var prevHash = Revocable.hashMsg(first);
var rotateMsg = Revocable.rotateLog(keyHashStr, doc.keys.validateKey, rotateUid, prevHash);
var signature = Revocable.signLog(rotateMsg, moderator.keys.edPrivate);
var rtConfig = {
creation: {
creatorEdPrivate: doc.keys.creator,
},
authentication: {
edPublic: moderator.edPublic,
edPrivate: moderator.edPrivate
},
metadata: {
validateKey: (secret.keys && secret.keys.validateKey) || undefined,
access: access,
revocableData: {
creatorKey: moderator.curvePublic,
creatorVKey: moderator.keys.edPublic,
rotate: {
uid: rotateUid,
hash: keyHashStr,
validateKey: doc.keys.validateKey,
signature: signature,
}
}
}
};
var rtConfig = data.rtConfig;
if (data.expire) { rtConfig.metadata.expire = data.expire; }
/*
@ -2261,11 +2164,6 @@ define([
}));
}
*/
console.error(doc);
console.error(moderator);
console.error(editor);
console.error(access);
console.error(keyHashStr);
startRealtime(rtConfig); // XXX