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

This commit is contained in:
yflory 2017-05-05 17:57:05 +02:00
commit bbc08bbcfd
16 changed files with 180 additions and 15 deletions

1
.gitignore vendored
View file

@ -13,3 +13,4 @@ data
npm-debug.log
pins/
blob/
privileged.conf

View file

@ -180,6 +180,31 @@ module.exports = {
*/
suppressRPCErrors: false,
/* WARNING: EXPERIMENTAL
*
* CryptPad features experimental support for encrypted file upload.
* Our encryption format is still liable to change. As such, we do not
* guarantee that files uploaded now will be supported in the future
*/
/* Setting this value to anything other than true will cause file upload
* attempts to be rejected outright.
*/
enableUploads: true,
/* If you have enabled file upload, you have the option of restricting it
* to a list of users identified by their public keys. If this value is set
* to true, your server will query a file (cryptpad/privileged.conf) when
* users connect via RPC. Only users whose public keys can be found within
* the file will be allowed to upload.
*
* privileged.conf uses '#' for line comments, and splits keys by newline.
* This is a temporary measure until a better quota system is in place.
* registered users' public keys can be found on the settings page.
*/
restrictUploads: true,
/* it is recommended that you serve cryptpad over https
* the filepaths below are used to configure your certificates
*/

View file

@ -114,7 +114,7 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>
</body>

View file

@ -111,7 +111,7 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>
</body>

View file

@ -233,7 +233,7 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>
</body>

View file

@ -132,7 +132,7 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>
</body>

View file

@ -39,5 +39,5 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>

View file

@ -115,7 +115,7 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>
</body>

View file

@ -333,6 +333,10 @@ define(function () {
out.settings_pinningError = "Something went wrong";
out.settings_usageAmount = "Your pinned pads occupy {0}MB";
out.settings_logoutEverywhereTitle = "Log out everywhere";
out.settings_logoutEverywhere = "Log out of all other web sessions";
out.settings_logoutEverywhereConfirm = "Are you sure? You will need to log in with all your devices.";
// index.html

View file

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "1.5.0",
"version": "1.6.0",
"dependencies": {
"express": "~4.10.1",
"ws": "^1.0.1",

View file

@ -54,6 +54,9 @@ These settings can be found in your configuration file in the `contentSecurity`
## Maintenance
Before upgrading your CryptPad instance to the latest version, we recommend that you check what has changed since your last update.
You can do so by checking which version you have (see package.json), and comparing it against newer [release notes](https://github.com/xwiki-labs/cryptpad/releases).
To get access to the most recent codebase:
```

58
rpc.js
View file

@ -409,6 +409,30 @@ var resetUserPins = function (store, Sessions, publicKey, channelList, cb) {
});
};
var getPrivilegedUserList = function (cb) {
Fs.readFile('./privileged.conf', 'utf8', function (e, body) {
if (e) {
if (e.code === 'ENOENT') {
return void cb(void 0, []);
}
return void (e.code);
}
var list = body.split(/\n/)
.map(function (line) {
return line.replace(/#.*$/, '').trim();
})
.filter(function (x) { return x; });
cb(void 0, list);
});
};
var isPrivilegedUser = function (publicKey, cb) {
getPrivilegedUserList(function (e, list) {
if (e) { return void cb(false); }
cb(list.indexOf(publicKey) !== -1);
});
};
var getLimit = function (cb) {
cb = cb; // TODO
};
@ -625,6 +649,11 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
return void Respond('INVALID_MSG');
}
var deny = function () {
Respond('E_ACCESS_DENIED');
};
var handleMessage = function (privileged) {
switch (msg[0]) {
case 'COOKIE': return void Respond(void 0);
case 'RESET':
@ -662,25 +691,54 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
Respond(void 0, dict);
});
// restricted to privileged users...
case 'UPLOAD':
if (!privileged) { return deny(); }
return void upload(blobStagingPath, Sessions, safeKey, msg[1], function (e, len) {
Respond(e, len);
});
case 'UPLOAD_STATUS':
if (!privileged) { return deny(); }
return void upload_status(blobStagingPath, Sessions, safeKey, function (e, stat) {
Respond(e, stat);
});
case 'UPLOAD_COMPLETE':
if (!privileged) { return deny(); }
return void upload_complete(blobStagingPath, blobPath, Sessions, safeKey, function (e, hash) {
Respond(e, hash);
});
case 'UPLOAD_CANCEL':
if (!privileged) { return deny(); }
return void upload_cancel(blobStagingPath, Sessions, safeKey, function (e) {
Respond(e);
});
default:
return void Respond('UNSUPPORTED_RPC_CALL', msg);
}
};
// reject uploads unless explicitly enabled
if (config.enableUploads !== true) {
return void handleMessage(false);
}
// restrict upload capability unless explicitly disabled
if (config.restrictUploads === false) {
return void handleMessage(true);
}
// if session has not been authenticated, do so
var session = Sessions[publicKey];
if (typeof(session.privilege) !== 'boolean') {
return void isPrivilegedUser(publicKey, function (yes) {
session.privilege = yes;
handleMessage(yes);
});
}
// if authenticated, proceed
handleMessage(session.privilege);
};
Store.create({

View file

@ -196,6 +196,7 @@ define([
[
userNameKey,
userHashKey,
'loginToken',
].forEach(function (k) {
sessionStorage.removeItem(k);
localStorage.removeItem(k);

View file

@ -134,6 +134,14 @@ define([
return ret;
};
var tryParsing = function (x) {
try { return JSON.parse(x); }
catch (e) {
console.error(e);
return null;
}
};
var onReady = function (f, proxy, Cryptpad, exp) {
var fo = exp.fo = FO.init(proxy.drive, {
Cryptpad: Cryptpad
@ -145,6 +153,37 @@ define([
f(void 0, store);
}
var requestLogin = function (Cryptpad) {
// log out so that you don't go into an endless loop...
Cryptpad.logout();
// redirect them to log in, and come back when they're done.
sessionStorage.redirectTo = window.location.href;
window.location.href = '/login/';
};
if (Cryptpad.isLoggedIn()) {
/* This isn't truly secure, since anyone who can read the user's object can
set their local loginToken to match that in the object. However, it exposes
a UI that will work most of the time. */
var tokenKey = 'loginToken';
// every user object should have a persistent, random number
if (typeof(proxy.loginToken) !== 'number') {
proxy[tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
}
var localToken = tryParsing(localStorage.getItem(tokenKey));
if (localToken === null) {
// if that number hasn't been set to localStorage, do so.
localStorage.setItem(tokenKey, proxy.loginToken);
} else if (localToken !== proxy[tokenKey]) {
// if it has been, and the local number doesn't match that in
// the user object, request that they reauthenticate.
return void requestLogin();
}
}
if (typeof(proxy.allowUserFeedback) !== 'boolean') {
proxy.allowUserFeedback = true;
}
@ -157,13 +196,7 @@ define([
// if the user is logged in, but does not have signing keys...
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
// log out so that you don't go into an endless loop...
Cryptpad.logout();
// redirect them to log in, and come back when they're done.
sessionStorage.redirectTo = window.location.href;
window.location.href = '/login/';
return;
return void requestLogin();
}
proxy.on('change', [Cryptpad.displayNameKey], function (o, n) {

View file

@ -105,7 +105,7 @@
</div>
</div>
</div>
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
</footer>
</body>

View file

@ -252,6 +252,42 @@ define([
return $div;
};
var createLogoutEverywhere = function (obj) {
var proxy = obj.proxy;
var $div = $('<div>', { 'class': 'logoutEverywhere', });
$('<label>', { 'for': 'logoutEverywhere'})
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
$('<br>').appendTo($div);
var $button = $('<button>', { id: 'logoutEverywhere', 'class': 'btn btn-primary' })
.text(Messages.settings_logoutEverywhere)
.appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
$button.click(function () {
var realtime = obj.info.realtime;
console.log(realtime);
Cryptpad.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
if (!yes) { return; }
$spinner.show();
$ok.hide();
var token = proxy.loginToken = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
localStorage.setItem('loginToken', token);
Cryptpad.whenRealtimeSyncs(realtime, function () {
$spinner.hide();
$ok.show();
window.setTimeout(function () {
$ok.fadeOut(1500);
}, 2500);
});
});
});
return $div;
};
var createImportLocalPads = function (obj) {
if (!Cryptpad.isLoggedIn()) { return; }
var $div = $('<div>', {'class': 'importLocalPads'});
@ -292,6 +328,10 @@ define([
APP.$container.append(createInfoBlock(obj));
APP.$container.append(createDisplayNameInput(obj));
APP.$container.append(createLanguageSelector());
if (Cryptpad.isLoggedIn()) {
APP.$container.append(createLogoutEverywhere(obj));
}
APP.$container.append(createResetTips());
APP.$container.append(createBackupDrive(obj));
APP.$container.append(createImportLocalPads(obj));