Merge branch 'staging' into restricted-registration

This commit is contained in:
ansuz 2021-05-01 02:04:22 +05:30
commit 30fc2a5edf
9 changed files with 156 additions and 21 deletions

View File

@ -5,6 +5,9 @@
* reminders in calendars * reminders in calendars
* import/export * import/export
* include LICENSE for ical.js * include LICENSE for ical.js
* translations
* out of BETA
* available from user admin menu
* use a specific version of bootstrap-tokenfield in bower.json * use a specific version of bootstrap-tokenfield in bower.json
* don't create readmes * don't create readmes
* support displaying a roadmap in static pages' footer * support displaying a roadmap in static pages' footer
@ -18,6 +21,12 @@
* lock sheets faster when applying checkpoints * lock sheets faster when applying checkpoints
* guard against undefined checkpoints * guard against undefined checkpoints
* don't spam users with prompts to checkpoints when they can't * don't spam users with prompts to checkpoints when they can't
* decrees
* SET_ADMIN_EMAIL
* SET_SUPPORT_MAILBOX
* Add DAPSI to our sponsor list
* checkup
* check for duplicate or incorrect headers
# 4.4.0 # 4.4.0

View File

@ -13,6 +13,10 @@ Core.isValidId = function (chan) {
[32, 33, 48].indexOf(chan.length) > -1; [32, 33, 48].indexOf(chan.length) > -1;
}; };
Core.isValidPublicKey = function (owner) {
return typeof(owner) === 'string' && owner.length === 44;
};
var makeToken = Core.makeToken = function () { var makeToken = Core.makeToken = function () {
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
.toString(16); .toString(16);

View File

@ -1,4 +1,5 @@
var Decrees = module.exports; var Decrees = module.exports;
var Core = require("./commands/core");
/* Admin decrees which modify global server state /* Admin decrees which modify global server state
@ -29,6 +30,10 @@ SET_LAST_BROADCAST_HASH
SET_SURVEY_URL SET_SURVEY_URL
SET_MAINTENANCE SET_MAINTENANCE
// EASIER CONFIG
SET_ADMIN_EMAIL
SET_SUPPORT_MAILBOX
NOT IMPLEMENTED: NOT IMPLEMENTED:
// RESTRICTED REGISTRATION // RESTRICTED REGISTRATION
@ -37,9 +42,11 @@ REVOKE_INVITE
REDEEM_INVITE REDEEM_INVITE
// 2.0 // 2.0
Env.adminEmail
Env.supportMailbox
Env.DEV_MODE || Env.FRESH_MODE, Env.DEV_MODE || Env.FRESH_MODE,
ADD_ADMIN_KEY
RM_ADMIN_KEY
*/ */
var commands = {}; var commands = {};
@ -88,6 +95,20 @@ var isNonNegativeNumber = function (n) {
}; };
*/ */
var default_validator = function () { return true; };
var makeGenericSetter = function (attr, validator) {
validator = validator || default_validator;
return function (Env, args) {
if (!validator(args)) {
throw new Error("INVALID_ARGS");
}
var value = args[0];
if (value === Env[attr]) { return false; }
Env[attr] = value;
return true;
};
};
var isInteger = function (n) { var isInteger = function (n) {
return !(typeof(n) !== 'number' || isNaN(n) || (n % 1) !== 0); return !(typeof(n) !== 'number' || isNaN(n) || (n % 1) !== 0);
}; };
@ -97,15 +118,7 @@ var args_isInteger = function (args) {
}; };
var makeIntegerSetter = function (attr) { var makeIntegerSetter = function (attr) {
return function (Env, args) { return makeGenericSetter(attr, args_isInteger);
if (!args_isInteger(args)) {
throw new Error('INVALID_ARGS');
}
var integer = args[0];
if (integer === Env[attr]) { return false; }
Env[attr] = integer;
return true;
};
}; };
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_MAX_UPLOAD_SIZE', [50 * 1024 * 1024]]], console.log) // CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_MAX_UPLOAD_SIZE', [50 * 1024 * 1024]]], console.log)
@ -130,6 +143,14 @@ var args_isString = function (args) {
return Array.isArray(args) && typeof(args[0]) === "string"; return Array.isArray(args) && typeof(args[0]) === "string";
}; };
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_ADMIN_EMAIL', ['admin@website.tld']]], console.log)
commands.SET_ADMIN_EMAIL = makeGenericSetter('adminEmail', args_isString);
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_SUPPORT_MAILBOX', ["Tdz6+fE9N9XXBY93rW5qeNa/k27yd40c0vq7EJyt7jA="]]], console.log)
commands.SET_SUPPORT_MAILBOX = makeGenericSetter('supportMailbox', function (args) {
return args_isString(args) && Core.isValidPublicKey(args[0]);
});
// Maintenance: Empty string or an object with a start and end time // Maintenance: Empty string or an object with a start and end time
var isNumber = function (value) { var isNumber = function (value) {
return typeof(value) === "number" && !isNaN(value); return typeof(value) === "number" && !isNaN(value);

View File

@ -1,4 +1,5 @@
var Meta = module.exports; var Meta = module.exports;
var Core = require("./commands/core");
var deduplicate = require("./common-util").deduplicateString; var deduplicate = require("./common-util").deduplicateString;
@ -35,9 +36,7 @@ the owners field is guaranteed to exist.
var commands = {}; var commands = {};
var isValidPublicKey = function (owner) { var isValidPublicKey = Core.isValidPublicKey;
return typeof(owner) === 'string' && owner.length === 44;
};
// isValidPublicKey is a better indication of what the above function does // isValidPublicKey is a better indication of what the above function does
// I'm preserving this function name in case we ever want to expand its // I'm preserving this function name in case we ever want to expand its

View File

@ -105,13 +105,15 @@ var setHeaders = (function () {
} }
if (Object.keys(headers).length) { if (Object.keys(headers).length) {
return function (req, res) { return function (req, res) {
// apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere // apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere
applyHeaderMap(res, { applyHeaderMap(res, {
"Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '', "Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '',
"Cross-Origin-Embedder-Policy": 'require-corp', "Cross-Origin-Embedder-Policy": 'require-corp',
}); });
if (Env.NO_SANDBOX) { if (Env.NO_SANDBOX) { // handles correct configuration for local development
// https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs
applyHeaderMap(res, { applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin', "Cross-Origin-Resource-Policy": 'cross-origin',
}); });
@ -120,11 +122,13 @@ var setHeaders = (function () {
// Don't set CSP headers on /api/config because they aren't necessary and they cause problems // Don't set CSP headers on /api/config because they aren't necessary and they cause problems
// when duplicated by NGINX in production environments // when duplicated by NGINX in production environments
if (/^\/api\/(broadcast|config)/.test(req.url)) { if (/^\/api\/(broadcast|config)/.test(req.url)) {
if (!Env.NO_SANDBOX) { /*
if (Env.NO_SANDBOX) {
applyHeaderMap(res, { applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin', "Cross-Origin-Resource-Policy": 'cross-origin',
}); });
} }
*/
return; return;
} }
applyHeaderMap(res, { applyHeaderMap(res, {

View File

@ -365,7 +365,7 @@ define([
}); });
assert(function (cb, msg) { assert(function (cb, msg) {
msg = msg; // XXX msg = msg;
return void cb(true); return void cb(true);
/* /*
msg.appendChild(h('span', [ msg.appendChild(h('span', [
@ -415,6 +415,58 @@ define([
}); });
}); });
var checkAPIHeaders = function (url, cb) {
$.ajax(url, {
dataType: 'text',
complete: function (xhr) {
var allHeaders = xhr.getAllResponseHeaders();
console.error(allHeaders);
var headers = {};
var duplicated = allHeaders.split('\n').some(function (header) {
var duplicate;
header.replace(/([^:]+):(.*)/, function (all, type, value) {
type = type.trim();
if (typeof(headers[type]) !== 'undefined') {
duplicate = true;
}
headers[type] = value.trim();
});
return duplicate;
});
if (duplicated) { return void cb(false); }
var expect = {
'cross-origin-resource-policy': 'cross-origin',
};
var incorrect = Object.keys(expect).some(function (k) {
var response = xhr.getResponseHeader(k);
if (response !== expect[k]) {
return true;
}
});
cb(!incorrect);
},
});
};
var INCORRECT_HEADER_TEXT = ' was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.';
assert(function (cb, msg) {
var url = '/api/config';
msg.innerText = url + INCORRECT_HEADER_TEXT;
checkAPIHeaders(url, cb);
});
assert(function (cb, msg) {
var url = '/api/broadcast';
msg.innerText = url + INCORRECT_HEADER_TEXT;
checkAPIHeaders(url, cb);
});
var row = function (cells) { var row = function (cells) {
return h('tr', cells.map(function (cell) { return h('tr', cells.map(function (cell) {
return h('td', cell); return h('td', cell);

View File

@ -798,7 +798,6 @@ define([
])).click(common.prepareFeedback(type)).click(function () { ])).click(common.prepareFeedback(type)).click(function () {
$(button).hide(); $(button).hide();
common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) { common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) {
waitingForStoringCb = false;
var error = err || (obj && obj.error); var error = err || (obj && obj.error);
if (error) { if (error) {
$(button).show(); $(button).show();

View File

@ -40,6 +40,7 @@ define([
Mermaid = _Mermaid; Mermaid = _Mermaid;
Mermaid.initialize({ Mermaid.initialize({
gantt: { axisFormat: '%m-%d', }, gantt: { axisFormat: '%m-%d', },
flowchart: { htmlLabels: false, },
theme: (window.CryptPad_theme === 'dark') ? 'dark' : 'default', theme: (window.CryptPad_theme === 'dark') ? 'dark' : 'default',
"themeCSS": mermaidThemeCSS, "themeCSS": mermaidThemeCSS,
}); });

View File

@ -386,11 +386,11 @@ define([
'tabindex': '-1', 'tabindex': '-1',
'data-icon': "fa-eye", 'data-icon': "fa-eye",
}, Messages.pad_mediatagPreview)), }, Messages.pad_mediatagPreview)),
h('li.cp-svg', h('a.cp-app-code-context-openin.dropdown-item', { h('li', h('a.cp-app-code-context-openin.dropdown-item', {
'tabindex': '-1', 'tabindex': '-1',
'data-icon': "fa-external-link", 'data-icon': "fa-external-link",
}, Messages.pad_mediatagOpen)), }, Messages.pad_mediatagOpen)),
h('li.cp-svg', h('a.cp-app-code-context-share.dropdown-item', { h('li', h('a.cp-app-code-context-share.dropdown-item', {
'tabindex': '-1', 'tabindex': '-1',
'data-icon': "fa-shhare-alt", 'data-icon': "fa-shhare-alt",
}, Messages.pad_mediatagShare)), }, Messages.pad_mediatagShare)),
@ -398,7 +398,7 @@ define([
'tabindex': '-1', 'tabindex': '-1',
'data-icon': "fa-cloud-upload", 'data-icon': "fa-cloud-upload",
}, Messages.pad_mediatagImport)), }, Messages.pad_mediatagImport)),
h('li', h('a.cp-app-code-context-download.dropdown-item', { h('li.cp-svg', h('a.cp-app-code-context-download.dropdown-item', {
'tabindex': '-1', 'tabindex': '-1',
'data-icon': "fa-download", 'data-icon': "fa-download",
}, Messages.download_mt_button)), }, Messages.download_mt_button)),
@ -429,6 +429,52 @@ define([
common.importMediaTag($mt); common.importMediaTag($mt);
} }
else if ($this.hasClass("cp-app-code-context-download")) { else if ($this.hasClass("cp-app-code-context-download")) {
if ($mt.is('pre.mermaid') || $mt.is('pre.markmap')) {
(function () {
var name = 'image.svg'; // XXX
var svg = $mt.find('svg')[0].cloneNode(true);
$(svg).attr('xmlns', 'http://www.w3.org/2000/svg').attr('width', $mt.width()).attr('height', $mt.height());
$(svg).find('foreignObject').each(function (i, el) {
var $el = $(el);
$el.find('br').after('\n');
$el.find('br').remove();
var t = $el[0].innerText || $el[0].textContent;
t.split('\n').forEach(function (text, i) {
var dy = (i+1)+'em';
$el.after(h('text', {y:0, dy:dy, style: ''}, text));
});
$el.remove();
});
var html = svg.outerHTML;
html = html.replace('<br>', '<br/>');
var b = new Blob([html], { type: 'image/svg+xml' });
window.saveAs(b, name);
})();
return;
}
if ($mt.is('pre.mathjax')) {
(function () {
var name = 'image.png'; // XXX
var svg = $mt.find('> span > svg')[0];
var clone = svg.cloneNode(true);
var html = clone.outerHTML;
var b = new Blob([html], { type: 'image/svg+xml' });
var blobURL = URL.createObjectURL(b);
var i = new Image();
i.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = i.width;
canvas.height = i.height;
var context = canvas.getContext('2d');
context.drawImage(i, 0, 0, i.width, i.height);
canvas.toBlob(function (blob) {
window.saveAs(blob, name);
});
};
i.src = blobURL;
})();
return;
}
var media = Util.find($mt, [0, '_mediaObject']); var media = Util.find($mt, [0, '_mediaObject']);
if (!media) { return void console.error('no media'); } if (!media) { return void console.error('no media'); }
if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); } if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); }