Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging
This commit is contained in:
commit
bbc08bbcfd
16 changed files with 180 additions and 15 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ data
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
pins/
|
pins/
|
||||||
blob/
|
blob/
|
||||||
|
privileged.conf
|
||||||
|
|
|
@ -180,6 +180,31 @@ module.exports = {
|
||||||
*/
|
*/
|
||||||
suppressRPCErrors: false,
|
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
|
/* it is recommended that you serve cryptpad over https
|
||||||
* the filepaths below are used to configure your certificates
|
* the filepaths below are used to configure your certificates
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -233,7 +233,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -39,5 +39,5 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -333,6 +333,10 @@ define(function () {
|
||||||
out.settings_pinningError = "Something went wrong";
|
out.settings_pinningError = "Something went wrong";
|
||||||
out.settings_usageAmount = "Your pinned pads occupy {0}MB";
|
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
|
// index.html
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "cryptpad",
|
"name": "cryptpad",
|
||||||
"description": "realtime collaborative visual editor with zero knowlege server",
|
"description": "realtime collaborative visual editor with zero knowlege server",
|
||||||
"version": "1.5.0",
|
"version": "1.6.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "~4.10.1",
|
"express": "~4.10.1",
|
||||||
"ws": "^1.0.1",
|
"ws": "^1.0.1",
|
||||||
|
|
|
@ -54,6 +54,9 @@ These settings can be found in your configuration file in the `contentSecurity`
|
||||||
|
|
||||||
## Maintenance
|
## 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:
|
To get access to the most recent codebase:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
58
rpc.js
58
rpc.js
|
@ -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) {
|
var getLimit = function (cb) {
|
||||||
cb = cb; // TODO
|
cb = cb; // TODO
|
||||||
};
|
};
|
||||||
|
@ -625,6 +649,11 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
||||||
return void Respond('INVALID_MSG');
|
return void Respond('INVALID_MSG');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deny = function () {
|
||||||
|
Respond('E_ACCESS_DENIED');
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleMessage = function (privileged) {
|
||||||
switch (msg[0]) {
|
switch (msg[0]) {
|
||||||
case 'COOKIE': return void Respond(void 0);
|
case 'COOKIE': return void Respond(void 0);
|
||||||
case 'RESET':
|
case 'RESET':
|
||||||
|
@ -662,19 +691,25 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
||||||
Respond(void 0, dict);
|
Respond(void 0, dict);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// restricted to privileged users...
|
||||||
case 'UPLOAD':
|
case 'UPLOAD':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
return void upload(blobStagingPath, Sessions, safeKey, msg[1], function (e, len) {
|
return void upload(blobStagingPath, Sessions, safeKey, msg[1], function (e, len) {
|
||||||
Respond(e, len);
|
Respond(e, len);
|
||||||
});
|
});
|
||||||
case 'UPLOAD_STATUS':
|
case 'UPLOAD_STATUS':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
return void upload_status(blobStagingPath, Sessions, safeKey, function (e, stat) {
|
return void upload_status(blobStagingPath, Sessions, safeKey, function (e, stat) {
|
||||||
Respond(e, stat);
|
Respond(e, stat);
|
||||||
});
|
});
|
||||||
case 'UPLOAD_COMPLETE':
|
case 'UPLOAD_COMPLETE':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
return void upload_complete(blobStagingPath, blobPath, Sessions, safeKey, function (e, hash) {
|
return void upload_complete(blobStagingPath, blobPath, Sessions, safeKey, function (e, hash) {
|
||||||
Respond(e, hash);
|
Respond(e, hash);
|
||||||
});
|
});
|
||||||
case 'UPLOAD_CANCEL':
|
case 'UPLOAD_CANCEL':
|
||||||
|
if (!privileged) { return deny(); }
|
||||||
return void upload_cancel(blobStagingPath, Sessions, safeKey, function (e) {
|
return void upload_cancel(blobStagingPath, Sessions, safeKey, function (e) {
|
||||||
Respond(e);
|
Respond(e);
|
||||||
});
|
});
|
||||||
|
@ -683,6 +718,29 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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({
|
Store.create({
|
||||||
filePath: pinPath,
|
filePath: pinPath,
|
||||||
}, function (s) {
|
}, function (s) {
|
||||||
|
|
|
@ -196,6 +196,7 @@ define([
|
||||||
[
|
[
|
||||||
userNameKey,
|
userNameKey,
|
||||||
userHashKey,
|
userHashKey,
|
||||||
|
'loginToken',
|
||||||
].forEach(function (k) {
|
].forEach(function (k) {
|
||||||
sessionStorage.removeItem(k);
|
sessionStorage.removeItem(k);
|
||||||
localStorage.removeItem(k);
|
localStorage.removeItem(k);
|
||||||
|
|
|
@ -134,6 +134,14 @@ define([
|
||||||
return ret;
|
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 onReady = function (f, proxy, Cryptpad, exp) {
|
||||||
var fo = exp.fo = FO.init(proxy.drive, {
|
var fo = exp.fo = FO.init(proxy.drive, {
|
||||||
Cryptpad: Cryptpad
|
Cryptpad: Cryptpad
|
||||||
|
@ -145,6 +153,37 @@ define([
|
||||||
f(void 0, store);
|
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') {
|
if (typeof(proxy.allowUserFeedback) !== 'boolean') {
|
||||||
proxy.allowUserFeedback = true;
|
proxy.allowUserFeedback = true;
|
||||||
}
|
}
|
||||||
|
@ -157,13 +196,7 @@ define([
|
||||||
|
|
||||||
// if the user is logged in, but does not have signing keys...
|
// if the user is logged in, but does not have signing keys...
|
||||||
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
|
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
|
||||||
// log out so that you don't go into an endless loop...
|
return void requestLogin();
|
||||||
Cryptpad.logout();
|
|
||||||
|
|
||||||
// redirect them to log in, and come back when they're done.
|
|
||||||
sessionStorage.redirectTo = window.location.href;
|
|
||||||
window.location.href = '/login/';
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.on('change', [Cryptpad.displayNameKey], function (o, n) {
|
proxy.on('change', [Cryptpad.displayNameKey], function (o, n) {
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-footer">CryptPad v1.5.0 (Fenrir)</div>
|
<div class="version-footer">CryptPad v1.6.0 (Grootslang)</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -252,6 +252,42 @@ define([
|
||||||
return $div;
|
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) {
|
var createImportLocalPads = function (obj) {
|
||||||
if (!Cryptpad.isLoggedIn()) { return; }
|
if (!Cryptpad.isLoggedIn()) { return; }
|
||||||
var $div = $('<div>', {'class': 'importLocalPads'});
|
var $div = $('<div>', {'class': 'importLocalPads'});
|
||||||
|
@ -292,6 +328,10 @@ define([
|
||||||
APP.$container.append(createInfoBlock(obj));
|
APP.$container.append(createInfoBlock(obj));
|
||||||
APP.$container.append(createDisplayNameInput(obj));
|
APP.$container.append(createDisplayNameInput(obj));
|
||||||
APP.$container.append(createLanguageSelector());
|
APP.$container.append(createLanguageSelector());
|
||||||
|
|
||||||
|
if (Cryptpad.isLoggedIn()) {
|
||||||
|
APP.$container.append(createLogoutEverywhere(obj));
|
||||||
|
}
|
||||||
APP.$container.append(createResetTips());
|
APP.$container.append(createResetTips());
|
||||||
APP.$container.append(createBackupDrive(obj));
|
APP.$container.append(createBackupDrive(obj));
|
||||||
APP.$container.append(createImportLocalPads(obj));
|
APP.$container.append(createImportLocalPads(obj));
|
||||||
|
|
Loading…
Reference in a new issue