Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging

pull/1/head
yflory 4 years ago
commit a5232b6cdb

@ -22,6 +22,7 @@ define([
Feedback, LocalStore, Messages, nThen, Block, Hash) { Feedback, LocalStore, Messages, nThen, Block, Hash) {
var Exports = { var Exports = {
Cred: Cred, Cred: Cred,
Block: Block,
// this is depended on by non-customizable files // this is depended on by non-customizable files
// be careful when modifying login.js // be careful when modifying login.js
requiredBytes: 192, requiredBytes: 192,
@ -92,7 +93,7 @@ define([
}; };
var loginOptionsFromBlock = function (blockInfo) { var loginOptionsFromBlock = Exports.loginOptionsFromBlock = function (blockInfo) {
var opt = {}; var opt = {};
var parsed = Hash.getSecrets('pad', blockInfo.User_hash); var parsed = Hash.getSecrets('pad', blockInfo.User_hash);
opt.channelHex = parsed.channel; opt.channelHex = parsed.channel;
@ -102,7 +103,7 @@ define([
return opt; return opt;
}; };
var loadUserObject = function (opt, cb) { var loadUserObject = Exports.loadUserObject = function (opt, cb) {
var config = { var config = {
websocketURL: NetConfig.getWebsocketURL(), websocketURL: NetConfig.getWebsocketURL(),
channel: opt.channelHex, channel: opt.channelHex,
@ -527,8 +528,6 @@ define([
if (!proxy[Constants.displayNameKey]) { if (!proxy[Constants.displayNameKey]) {
proxy[Constants.displayNameKey] = uname; proxy[Constants.displayNameKey] = uname;
} }
LocalStore.eraseTempSessionValues();
if (result.blockHash) { if (result.blockHash) {
LocalStore.setBlockHash(result.blockHash); LocalStore.setBlockHash(result.blockHash);

@ -20,6 +20,12 @@ html, body {
padding-top: 15px; padding-top: 15px;
} }
.pending {
border: 1px solid white;
.fa {
margin-right: 20px;
}
}
.success { .success {
border: 1px solid green; border: 1px solid green;
} }
@ -53,5 +59,9 @@ html, body {
background-color: @cp_alerts-danger-bg; background-color: @cp_alerts-danger-bg;
color: @cp_alerts-danger-text; color: @cp_alerts-danger-text;
} }
iframe {
display: none;
}
} }

@ -21,8 +21,10 @@ define([], function () {
}); });
}; };
assert.run = function (cb) { assert.run = function (cb, progress) {
progress = progress || function () {};
var count = ASSERTS.length; var count = ASSERTS.length;
var total = ASSERTS.length;
var done = function (err) { var done = function (err) {
count--; count--;
if (err) { failMessages.push(err); } if (err) { failMessages.push(err); }
@ -38,6 +40,7 @@ define([], function () {
ASSERTS.forEach(function (f, index) { ASSERTS.forEach(function (f, index) {
f(function (err) { f(function (err) {
//console.log("test " + index); //console.log("test " + index);
progress(index, total);
done(err, index); done(err, index);
}, index); }, index);
}); });

@ -6,4 +6,6 @@
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script> <script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
</head> </head>
<body> <body>
<div id="cp-progress"></div>
<iframe-placeholder>

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html class="cp-app-noscroll cp-app-print">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script async data-bootload="/checkup/inner.js" data-main="/common/sframe-boot.js?ver=1.7" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
</head>
<body class="cp-app-checkup">
</body>
</html>

@ -0,0 +1,4 @@
define([
], function () {
console.log('inner loaded');
});

@ -4,12 +4,21 @@ define([
'/assert/assertions.js', '/assert/assertions.js',
'/common/hyperscript.js', '/common/hyperscript.js',
'/customize/messages.js', '/customize/messages.js',
'/common/dom-ready.js',
'/bower_components/nthen/index.js',
'/common/sframe-common-outer.js', '/common/sframe-common-outer.js',
'/customize/login.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/pinpad.js',
'/common/outer/network-config.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/pages/page-checkup.less', 'less!/customize/src/less2/pages/page-checkup.less',
], function ($, ApiConfig, Assertions, h, Messages /*, SFCommonO*/) { ], function ($, ApiConfig, Assertions, h, Messages, DomReady,
nThen, SFCommonO, Login, Hash, Util, Pinpad,
NetConfig) {
var assert = Assertions(); var assert = Assertions();
var trimSlashes = function (s) { var trimSlashes = function (s) {
@ -41,7 +50,7 @@ define([
var checkAvailability = function (url, cb) { var checkAvailability = function (url, cb) {
$.ajax({ $.ajax({
url: url, url: url,
date: {}, data: {},
complete: function (xhr) { complete: function (xhr) {
cb(xhr.status === 200); cb(xhr.status === 200);
}, },
@ -52,10 +61,141 @@ define([
checkAvailability(trimmedUnsafe, cb); checkAvailability(trimmedUnsafe, cb);
}, _alert("Main domain is not available")); }, _alert("Main domain is not available"));
// Try loading an iframe on the safe domain
assert(function (cb) {
var to;
nThen(function (waitFor) {
DomReady.onReady(waitFor());
}).nThen(function (waitFor) {
to = setTimeout(function () {
console.error('TIMEOUT loading iframe on the safe domain');
cb(false);
}, 5000);
SFCommonO.initIframe(waitFor);
}).nThen(function () {
// Iframe is loaded
clearTimeout(to);
cb(true);
});
}, _alert("Sandbox domain is not available"));
// Test Websocket
var evWSError = Util.mkEvent(true);
assert(function (cb) { assert(function (cb) {
console.log(trimmedSafe); var ws = new WebSocket(NetConfig.getWebsocketURL());
checkAvailability(trimmedSafe, cb); var to = setTimeout(function () {
}, _alert("Sandbox domain is not available")); // FIXME Blocked by CSP. try loading it via sframe ? console.error('Websocket TIMEOUT');
evWSError.fire();
cb('TIMEOUT (5 seconds)');
}, 5000);
ws.onopen = function () {
clearTimeout(to);
cb(true);
};
ws.onerror = function (err) {
clearTimeout(to);
console.error('Websocket error', err);
evWSError.fire();
cb('WebSocket error: check your console');
};
}, _alert("Websocket is not available"));
// Test login block
assert(function (cb) {
var bytes = new Uint8Array(Login.requiredBytes);
var opt = Login.allocateBytes(bytes);
var blockUrl = Login.Block.getBlockUrl(opt.blockKeys);
var blockRequest = Login.Block.serialize("{}", opt.blockKeys);
var removeRequest = Login.Block.remove(opt.blockKeys);
console.log('Test block URL:', blockUrl);
var userHash = '/2/drive/edit/000000000000000000000000';
var secret = Hash.getSecrets('drive', userHash);
opt.keys = secret.keys;
opt.channelHex = secret.channel;
var RT, rpc, exists;
nThen(function (waitFor) {
Util.fetch(blockUrl, waitFor(function (err) {
if (err) { return; } // No block found
exists = true;
}));
}).nThen(function (waitFor) {
// If WebSockets aren't working, don't wait forever here
evWSError.reg(function () {
waitFor.abort();
cb("No WebSocket (test number 6)");
});
// Create proxy
Login.loadUserObject(opt, waitFor(function (err, rt) {
if (err) {
waitFor.abort();
console.error("Can't create new channel. This may also be a websocket issue.");
return void cb(false);
}
RT = rt;
var proxy = rt.proxy;
proxy.edPublic = opt.edPublic;
proxy.edPrivate = opt.edPrivate;
proxy.curvePublic = opt.curvePublic;
proxy.curvePrivate = opt.curvePrivate;
rt.realtime.onSettle(waitFor());
}));
}).nThen(function (waitFor) {
// Init RPC
Pinpad.create(RT.network, RT.proxy, waitFor(function (e, _rpc) {
if (e) {
waitFor.abort();
console.error("Can't initialize RPC", e); // INVALID_KEYS
return void cb(false);
}
rpc = _rpc;
}));
}).nThen(function (waitFor) {
// Write block
if (exists) { return; }
rpc.writeLoginBlock(blockRequest, waitFor(function (e) {
if (e) {
waitFor.abort();
console.error("Can't write login block", e);
return void cb(false);
}
}));
}).nThen(function (waitFor) {
// Read block
Util.fetch(blockUrl, waitFor(function (e) {
if (e) {
waitFor.abort();
console.error("Can't read login block", e);
return void cb(false);
}
}));
}).nThen(function (waitFor) {
// Remove block
rpc.removeLoginBlock(removeRequest, waitFor(function (e) {
if (e) {
waitFor.abort();
console.error("Can't remove login block", e);
console.error(blockRequest);
return void cb(false);
}
}));
}).nThen(function (waitFor) {
rpc.removeOwnedChannel(secret.channel, waitFor(function (e) {
if (e) {
waitFor.abort();
console.error("Can't remove channel", e);
return void cb(false);
}
}));
}).nThen(function () {
cb(true);
});
}, _alert("Login block is not working (write/read/remove)"));
var row = function (cells) { var row = function (cells) {
return h('tr', cells.map(function (cell) { return h('tr', cells.map(function (cell) {
@ -73,6 +213,8 @@ define([
]); ]);
}; };
var completed = 0;
var $progress = $('#cp-progress');
assert.run(function (state) { assert.run(function (state) {
var errors = state.errors; var errors = state.errors;
var failed = errors.length; var failed = errors.length;
@ -94,6 +236,17 @@ define([
h('div.failures', errors.map(failureReport)), h('div.failures', errors.map(failureReport)),
]); ]);
$progress.remove();
$('body').prepend(report); $('body').prepend(report);
}, function (i, total) {
console.log('test '+ i +' completed');
completed++;
Messages.assert_numberOfTestsCompleted = "{0} / {1} tests completed.";
$progress.html('').append(h('div.report.pending.summary', [
h('p', [
h('i.fa.fa-spinner.fa-pulse'),
h('span', Messages._getKey('assert_numberOfTestsCompleted', [completed, total]))
])
]));
}); });
}); });

@ -232,17 +232,6 @@ define([
}; };
postMessage("MIGRATE_ANON_DRIVE", data, cb); postMessage("MIGRATE_ANON_DRIVE", data, cb);
}; };
// Settings
common.deleteAccount = function (cb) {
postMessage("DELETE_ACCOUNT", null, function (obj) {
if (obj.state) {
Feedback.send('DELETE_ACCOUNT_AUTOMATIC');
} else {
Feedback.send('DELETE_ACCOUNT_MANUAL');
}
cb(obj);
});
};
// Drive // Drive
common.userObjectCommand = function (data, cb) { common.userObjectCommand = function (data, cb) {
postMessage("DRIVE_USEROBJECT", data, cb); postMessage("DRIVE_USEROBJECT", data, cb);
@ -1674,30 +1663,16 @@ define([
}; };
common.changeUserPassword = function (Crypt, edPublic, data, cb) { var getBlockKeys = function (data, cb) {
if (!edPublic) {
return void cb({
error: 'E_NOT_LOGGED_IN'
});
}
var accountName = LocalStore.getAccountName(); var accountName = LocalStore.getAccountName();
var hash = LocalStore.getUserHash(); var password = data.password;
if (!hash) { var Cred, Block, Login;
return void cb({ var blockKeys;
error: 'E_NOT_LOGGED_IN'
});
}
var password = data.password; // To remove your old block
var newPassword = data.newPassword; // To create your new block
var secret = Hash.getSecrets('drive', hash);
var newHash, newHref, newSecret, blockKeys;
var oldIsOwned = false;
var hash = LocalStore.getUserHash();
if (!hash) { return void cb({ error: 'E_NOT_LOGGED_IN' }); }
var blockHash = LocalStore.getBlockHash(); var blockHash = LocalStore.getBlockHash();
var oldBlockKeys;
var Cred, Block, Login;
Nthen(function (waitFor) { Nthen(function (waitFor) {
require([ require([
'/common/common-credential.js', '/common/common-credential.js',
@ -1710,30 +1685,92 @@ define([
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// confirm that the provided password is correct // confirm that the provided password is correct
Cred.deriveFromPassphrase(accountName, password, Login.requiredBytes, waitFor(function (bytes) { Cred.deriveFromPassphrase(accountName, password, Login.requiredBytes,
waitFor(function (bytes) {
var allocated = Login.allocateBytes(bytes); var allocated = Login.allocateBytes(bytes);
oldBlockKeys = allocated.blockKeys; blockKeys = allocated.blockKeys;
if (blockHash) { if (blockHash) {
if (blockHash !== allocated.blockHash) { if (blockHash !== allocated.blockHash) {
// incorrect password
console.log("provided password did not yield the correct blockHash"); console.log("provided password did not yield the correct blockHash");
// incorrect password probably
waitFor.abort(); waitFor.abort();
return void cb({ return void cb({ error: 'INVALID_PASSWORD', });
error: 'INVALID_PASSWORD',
});
} }
// the user has already created a block, so you should compare against that
} else { } else {
// otherwise they're a legacy user, and we should check against the User_hash // otherwise they're a legacy user, and we should check against the User_hash
if (hash !== allocated.userHash) { if (hash !== allocated.userHash) {
// incorrect password
console.log("provided password did not yield the correct userHash"); console.log("provided password did not yield the correct userHash");
waitFor.abort(); waitFor.abort();
return void cb({ return void cb({ error: 'INVALID_PASSWORD', });
error: 'INVALID_PASSWORD',
});
} }
} }
})); }));
}).nThen(function () {
cb({
Cred: Cred,
Block: Block,
Login: Login,
blockKeys: blockKeys
});
});
};
common.deleteAccount = function (data, cb) {
data = data || {};
// Confirm that the provided password is corrct and get the block keys
getBlockKeys(data, function (obj) {
if (obj && obj.error) { return void cb(obj); }
var blockKeys = obj.blockKeys;
var removeData = obj.Block.remove(blockKeys);
postMessage("DELETE_ACCOUNT", {
removeData: removeData
}, function (obj) {
if (obj.state) {
Feedback.send('DELETE_ACCOUNT_AUTOMATIC');
} else {
Feedback.send('DELETE_ACCOUNT_MANUAL');
}
cb(obj);
});
});
};
common.changeUserPassword = function (Crypt, edPublic, data, cb) {
if (!edPublic) {
return void cb({
error: 'E_NOT_LOGGED_IN'
});
}
var accountName = LocalStore.getAccountName();
var hash = LocalStore.getUserHash();
if (!hash) {
return void cb({
error: 'E_NOT_LOGGED_IN'
});
}
var password = data.password; // To remove your old block
var newPassword = data.newPassword; // To create your new block
var secret = Hash.getSecrets('drive', hash);
var newHash, newHref, newSecret, blockKeys;
var oldIsOwned = false;
var blockHash = LocalStore.getBlockHash();
var oldBlockKeys;
var Cred, Block, Login;
Nthen(function (waitFor) {
getBlockKeys(data, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
return void cb(obj);
}
oldBlockKeys = obj.blockKeys;
Cred = obj.Cred;
Login = obj.Login;
Block = obj.Block;
}));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Check if our drive is already owned // Check if our drive is already owned
console.log("checking if old drive is owned"); console.log("checking if old drive is owned");

@ -741,6 +741,7 @@ define([
Store.deleteAccount = function (clientId, data, cb) { Store.deleteAccount = function (clientId, data, cb) {
var edPublic = store.proxy.edPublic; var edPublic = store.proxy.edPublic;
var removeData = data && data.removeData;
Store.anonRpcMsg(clientId, { Store.anonRpcMsg(clientId, {
msg: 'GET_METADATA', msg: 'GET_METADATA',
data: store.driveChannel data: store.driveChannel
@ -769,8 +770,11 @@ define([
channel: store.driveChannel, channel: store.driveChannel,
force: true force: true
}, waitFor()); }, 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());
}).nThen(function () { }).nThen(function () {
// TODO delete block
// Log out current worker // Log out current worker
postMessage(clientId, "DELETE_ACCOUNT", token, function () {}); postMessage(clientId, "DELETE_ACCOUNT", token, function () {});
store.network.disconnect(); store.network.disconnect();

@ -85,7 +85,7 @@
} }
} }
.cp-settings-change-password, .cp-settings-own-drive { .cp-settings-change-password, .cp-settings-own-drive, .cp-settings-delete {
[type="password"], [type="text"] { [type="password"], [type="text"] {
width: @sidebar_button-width; width: @sidebar_button-width;
flex: unset; flex: unset;

@ -469,63 +469,65 @@ define([
}); });
}, true); }, true);
create['delete'] = function() { makeBlock('delete', function(cb) { // Msg.settings_deleteHint, .settings_deleteTitle
if (!common.isLoggedIn()) { return; } if (!common.isLoggedIn()) { return cb(false); }
var $div = $('<div>', { 'class': 'cp-settings-delete cp-sidebarlayout-element' });
$('<span>', { 'class': 'label' }).text(Messages.settings_deleteTitle).appendTo($div);
$('<span>', { 'class': 'cp-sidebarlayout-description' }) var button = h('button.btn.btn-danger', Messages.settings_deleteButton);
.append(Messages.settings_deleteHint).appendTo($div); var form = h('div', [
UI.passwordInput({
var $ok = $('<span>', { 'class': 'fa fa-check', title: Messages.saved }); id: 'cp-settings-delete-account',
var $spinner = $('<span>', { 'class': 'fa fa-spinner fa-pulse' }); placeholder: Messages.settings_changePasswordCurrent
}, true),
var $button = $('<button>', { 'id': 'cp-settings-delete', 'class': 'btn btn-danger' }) button
.text(Messages.settings_deleteButton).appendTo($div); ]);
var $form = $(form);
var $button = $(button);
var spinner = UI.makeSpinner($form);
$button.click(function() { UI.confirmButton(button, {
$spinner.show(); classes: 'btn-danger',
UI.confirm(Messages.settings_deleteConfirm, function(yes) { multiple: true
if (!yes) { return void $spinner.hide(); } }, function() {
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function(err, data) { $button.prop('disabled', 'disabled');
// Owned drive var password = $form.find('#cp-settings-delete-account').val();
if (data.state === true) { if (!password) {
sframeChan.query('Q_SETTINGS_LOGOUT', null, function() {}); return void UI.warn(Messages.error);
UI.alert(Messages.settings_deleted, function() { }
common.gotoURL('/'); spinner.spin();
}); sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", {
$ok.show(); password: password
$spinner.hide(); }, function(err, data) {
return; if (data && data.error) {
spinner.hide();
$button.prop('disabled', '');
if (data.error === 'INVALID_PASSWORD') {
return void UI.warn(Messages.drive_sfPasswordError);
} }
// Not owned drive console.error(data.error);
var msg = h('div.cp-app-settings-delete-alert', [ return void UI.warn(Messages.error);
h('p', Messages.settings_deleteModal), }
h('pre', JSON.stringify(data, 0, 2)) // Owned drive
]); if (data.state === true) {
UI.alert(msg); sframeChan.query('Q_SETTINGS_LOGOUT', null, function() {});
$spinner.hide(); UI.alert(Messages.settings_deleted, function() {
}); common.gotoURL('/');
});
spinner.done();
return;
}
// Not owned drive
var msg = h('div.cp-app-settings-delete-alert', [
h('p', Messages.settings_deleteModal),
h('pre', JSON.stringify(data, 0, 2))
]);
UI.alert(msg);
spinner.hide();
$button.prop('disabled', '');
}); });
// TODO
/*
UI.confirm("Are you sure?", function (yes) {
// Logout everywhere
// Disconnect other tabs
// Remove owned pads
// Remove owned drive
// Remove pinstore
// Alert: "Account deleted", press OK to be redirected to the home page
$spinner.hide();
});*/
}); });
$spinner.hide().appendTo($div); cb(form);
$ok.hide().appendTo($div); }, true);
return $div;
};
create['change-password'] = function() { create['change-password'] = function() {
if (!common.isLoggedIn()) { return; } if (!common.isLoggedIn()) { return; }

@ -67,7 +67,7 @@ define([
Cryptpad.mergeAnonDrive(cb); Cryptpad.mergeAnonDrive(cb);
}); });
sframeChan.on('Q_SETTINGS_DELETE_ACCOUNT', function (data, cb) { sframeChan.on('Q_SETTINGS_DELETE_ACCOUNT', function (data, cb) {
Cryptpad.deleteAccount(cb); Cryptpad.deleteAccount(data, cb);
}); });
sframeChan.on('Q_COLORTHEME_CHANGE', function (data, cb) { sframeChan.on('Q_COLORTHEME_CHANGE', function (data, cb) {
localStorage['CRYPTPAD_STORE|colortheme'] = data.theme; localStorage['CRYPTPAD_STORE|colortheme'] = data.theme;

Loading…
Cancel
Save