From 294a444603ada0804755a9ccb39a8989623be5d2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 Feb 2020 13:09:12 -0500 Subject: [PATCH 01/14] WIP removing defaults from the example config file --- config/config.example.js | 56 ++++------------------------------ lib/defaults.js | 65 ++++++++++++++++++++++++++++++++++++++++ lib/load-config.js | 2 +- 3 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 lib/defaults.js diff --git a/config/config.example.js b/config/config.example.js index 90e96a66a..e2bcea5bc 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -16,37 +16,7 @@ var _domain = 'http://localhost:3000/'; // requiring admins to preserve it is unnecessarily confusing var domain = ' ' + _domain; -// Content-Security-Policy -var baseCSP = [ - "default-src 'none'", - "style-src 'unsafe-inline' 'self' " + domain, - "font-src 'self' data:" + domain, - - /* child-src is used to restrict iframes to a set of allowed domains. - * connect-src is used to restrict what domains can connect to the websocket. - * - * it is recommended that you configure these fields to match the - * domain which will serve your CryptPad instance. - */ - "child-src blob: *", - // IE/Edge - "frame-src blob: *", - - /* this allows connections over secure or insecure websockets - if you are deploying to production, you'll probably want to remove - the ws://* directive, and change '*' to your domain - */ - "connect-src 'self' ws: wss: blob:" + domain, - - // data: is used by codemirror - "img-src 'self' data: blob:" + domain, - "media-src * blob:", - - // for accounts.cryptpad.fr authentication and cross-domain iframe sandbox - "frame-ancestors *", - "" -]; - +var Default = require("../lib/defaults"); module.exports = { /* ===================== @@ -113,34 +83,18 @@ module.exports = { * These settings may vary widely depending on your needs * Examples are provided below */ - httpHeaders: { - "X-XSS-Protection": "1; mode=block", - "X-Content-Type-Options": "nosniff", - "Access-Control-Allow-Origin": "*" - }, + httpHeaders: Default.httpHeaders(), - contentSecurity: baseCSP.join('; ') + - "script-src 'self'" + domain, + contentSecurity: Default.contentSecurity(domain), // CKEditor and OnlyOffice require significantly more lax content security policy in order to function. - padContentSecurity: baseCSP.join('; ') + - "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain, + padContentSecurity: Default.padContentSecurity(domain), /* Main pages * add exceptions to the router so that we can access /privacy.html * and other odd pages */ - mainPages: [ - 'index', - 'privacy', - 'terms', - 'about', - 'contact', - 'what-is-cryptpad', - 'features', - 'faq', - 'maintenance' - ], + mainPages: Default.mainPages(), /* ===================== * Subscriptions diff --git a/lib/defaults.js b/lib/defaults.js new file mode 100644 index 000000000..f5d87b20b --- /dev/null +++ b/lib/defaults.js @@ -0,0 +1,65 @@ +var Default = module.exports; + +Default.commonCSP = function (domain) { + // Content-Security-Policy + return [ + "default-src 'none'", + "style-src 'unsafe-inline' 'self' " + domain, + "font-src 'self' data:" + domain, + + /* child-src is used to restrict iframes to a set of allowed domains. + * connect-src is used to restrict what domains can connect to the websocket. + * + * it is recommended that you configure these fields to match the + * domain which will serve your CryptPad instance. + */ + "child-src blob: *", + // IE/Edge + "frame-src blob: *", + + /* this allows connections over secure or insecure websockets + if you are deploying to production, you'll probably want to remove + the ws://* directive, and change '*' to your domain + */ + "connect-src 'self' ws: wss: blob:" + domain, + + // data: is used by codemirror + "img-src 'self' data: blob:" + domain, + "media-src * blob:", + + // for accounts.cryptpad.fr authentication and cross-domain iframe sandbox + "frame-ancestors *", + "" + ]; +}; + +Default.contentSecurity = function (domain) { + return Default.commonCSP(domain).join('; ') + "script-src 'self'" + domain; +}; + +Default.padContentSecurity = function (domain) { + return Default.commonCSP(domain).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain; +}; + +Default.httpHeaders = function () { + return { + "X-XSS-Protection": "1; mode=block", + "X-Content-Type-Options": "nosniff", + "Access-Control-Allow-Origin": "*" + }; +}; + +Default.mainPages = function () { + return [ + 'index', + 'privacy', + 'terms', + 'about', + 'contact', + 'what-is-cryptpad', + 'features', + 'faq', + 'maintenance' + ]; +}; + diff --git a/lib/load-config.js b/lib/load-config.js index 0756c2df4..1c502226a 100644 --- a/lib/load-config.js +++ b/lib/load-config.js @@ -1,7 +1,7 @@ /* jslint node: true */ "use strict"; var config; -var configPath = process.env.CRYPTPAD_CONFIG || "../config/config"; +var configPath = process.env.CRYPTPAD_CONFIG || "../config/config.js"; try { config = require(configPath); if (config.adminEmail === 'i.did.not.read.my.config@cryptpad.fr') { From 3df47a1415fed91e9a795199d1f49d0ff8f98e96 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 27 Feb 2020 13:24:19 -0500 Subject: [PATCH 02/14] continued WIP config reorganization --- config/config.example.js | 67 ++++++++++++++-------------------------- lib/defaults.js | 19 ++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index e2bcea5bc..0754b325b 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -19,6 +19,30 @@ var domain = ' ' + _domain; var Default = require("../lib/defaults"); module.exports = { + /* Sales coming from your server will be identified by your domain + * + * If you are using CryptPad in a business context, please consider taking a support contract + * by contacting sales@cryptpad.fr + */ + myDomain: _domain, + + // the address you want to bind to, :: means all ipv4 and ipv6 addresses + // this may not work on all operating systems + httpAddress: '::', + + // the port on which your httpd will listen + httpPort: 3000, + + // This is for allowing the cross-domain iframe to function when developing + httpSafePort: 3001, + + // This is for deployment in production, CryptPad uses a separate origin (domain) to host the + // cross-domain iframe. It can simply host the same content as CryptPad. + // httpSafeOrigin: "https://some-other-domain.xyz", + + httpUnsafeOrigin: domain, + + /* ===================== * Admin * ===================== */ @@ -51,22 +75,6 @@ module.exports = { * Infra setup * ===================== */ - // the address you want to bind to, :: means all ipv4 and ipv6 addresses - // this may not work on all operating systems - httpAddress: '::', - - // the port on which your httpd will listen - httpPort: 3000, - - // This is for allowing the cross-domain iframe to function when developing - httpSafePort: 3001, - - // This is for deployment in production, CryptPad uses a separate origin (domain) to host the - // cross-domain iframe. It can simply host the same content as CryptPad. - // httpSafeOrigin: "https://some-other-domain.xyz", - - httpUnsafeOrigin: domain, - /* Your CryptPad server will share this value with clients * via its /api/config endpoint. * @@ -127,13 +135,6 @@ module.exports = { */ adminEmail: 'i.did.not.read.my.config@cryptpad.fr', - /* Sales coming from your server will be identified by your domain - * - * If you are using CryptPad in a business context, please consider taking a support contract - * by contacting sales@cryptpad.fr - */ - myDomain: _domain, - /* * If you are using CryptPad internally and you want to increase the per-user storage limit, * change the following value. @@ -171,12 +172,6 @@ module.exports = { * STORAGE * ===================== */ - /* By default the CryptPad server will run scheduled tasks every five minutes - * If you want to run scheduled tasks in a separate process (like a crontab) - * you can disable this behaviour by setting the following value to true - */ - disableIntegratedTasks: false, - /* Pads that are not 'pinned' by any registered user can be set to expire * after a configurable number of days of inactivity (default 90 days). * The value can be changed or set to false to remove expiration. @@ -205,20 +200,6 @@ module.exports = { */ maxUploadSize: 20 * 1024 * 1024, - /* ===================== - * HARDWARE RELATED - * ===================== */ - - /* CryptPad's file storage adaptor closes unused files after a configurable - * number of milliseconds (default 30000 (30 seconds)) - */ - channelExpirationMs: 30000, - - /* CryptPad's file storage adaptor is limited by the number of open files. - * When the adaptor reaches openFileLimit, it will clean up older files - */ - openFileLimit: 2048, - /* ===================== * DATABASE VOLUMES * ===================== */ diff --git a/lib/defaults.js b/lib/defaults.js index f5d87b20b..fc807a2c7 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -63,3 +63,22 @@ Default.mainPages = function () { ]; }; +/* By default the CryptPad server will run scheduled tasks every five minutes + * If you want to run scheduled tasks in a separate process (like a crontab) + * you can disable this behaviour by setting the following value to true + */ + //disableIntegratedTasks: false, + + /* CryptPad's file storage adaptor closes unused files after a configurable + * number of milliseconds (default 30000 (30 seconds)) + */ +// channelExpirationMs: 30000, + + /* CryptPad's file storage adaptor is limited by the number of open files. + * When the adaptor reaches openFileLimit, it will clean up older files + */ + //openFileLimit: 2048, + + + + From 60862d9f87960c86b01fc95536c1b578259f5659 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 28 Feb 2020 15:48:50 +0100 Subject: [PATCH 03/14] Fix isLoggedIn issue --- www/common/outer/async-store.js | 1 + www/common/sframe-common-outer.js | 2 +- www/common/sframe-common.js | 2 +- www/filepicker/main.js | 2 +- www/share/main.js | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index cb4aa78a3..b3c069ca3 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -583,6 +583,7 @@ define([ support: Util.find(store.proxy, ['mailboxes', 'support', 'channel']), pendingFriends: store.proxy.friends_pending || {}, supportPrivateKey: Util.find(store.proxy, ['mailboxes', 'supportadmin', 'keys', 'curvePrivate']), + accountName: store.proxy.login_name || '', teams: teams, plan: account.plan } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 144da1c61..2049fc2b5 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -423,7 +423,7 @@ define([ }; var additionalPriv = { app: parsed.type, - accountName: Utils.LocalStore.getAccountName(), + loggedIn: Utils.LocalStore.isLoggedIn(), origin: window.location.origin, pathname: window.location.pathname, fileHost: ApiConfig.fileHost, diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 1a713494c..8d04c6263 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -69,7 +69,7 @@ define([ funcs.getAppConfig = function () { return AppConfig; }; funcs.isLoggedIn = function () { - return ctx.metadataMgr.getPrivateData().accountName; + return ctx.metadataMgr.getPrivateData().loggedIn; }; // MISC diff --git a/www/filepicker/main.js b/www/filepicker/main.js index cada394ba..e63ab457c 100644 --- a/www/filepicker/main.js +++ b/www/filepicker/main.js @@ -84,7 +84,7 @@ define([ metaObj.doc = {}; var additionalPriv = { fileHost: ApiConfig.fileHost, - accountName: Utils.LocalStore.getAccountName(), + loggedIn: Utils.LocalStore.isLoggedIn(), origin: window.location.origin, pathname: window.location.pathname, feedbackAllowed: Utils.Feedback.state, diff --git a/www/share/main.js b/www/share/main.js index 28cc1a882..adbd69e05 100644 --- a/www/share/main.js +++ b/www/share/main.js @@ -86,7 +86,7 @@ define([ metaObj.doc = {}; var additionalPriv = { fileHost: ApiConfig.fileHost, - accountName: Utils.LocalStore.getAccountName(), + loggedIn: Utils.LocalStore.isLoggedIn(), origin: window.location.origin, pathname: window.location.pathname, feedbackAllowed: Utils.Feedback.state, From 3cf09924aec87b7bd98876c54c19cd84d25d3213 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 10:46:44 -0500 Subject: [PATCH 04/14] fix default CSP headers --- lib/defaults.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/defaults.js b/lib/defaults.js index fc807a2c7..6d8ec5e04 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -1,7 +1,9 @@ var Default = module.exports; Default.commonCSP = function (domain) { + domain = ' ' + domain; // Content-Security-Policy + return [ "default-src 'none'", "style-src 'unsafe-inline' 'self' " + domain, @@ -34,11 +36,11 @@ Default.commonCSP = function (domain) { }; Default.contentSecurity = function (domain) { - return Default.commonCSP(domain).join('; ') + "script-src 'self'" + domain; + return (Default.commonCSP(domain).join('; ') + "script-src 'self' " + domain).replace(/\s+/g, ' '); }; Default.padContentSecurity = function (domain) { - return Default.commonCSP(domain).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + domain; + return (Default.commonCSP(domain).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline' " + domain).replace(/\s+/g, ' '); }; Default.httpHeaders = function () { From 94d8e7f2c2d8c3065e8cfa14db6c7dcaf15a7487 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 10:49:20 -0500 Subject: [PATCH 05/14] provide a consistent type for 'customLimits' --- lib/historyKeeper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index ed67602bd..95cb43e56 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -68,7 +68,7 @@ module.exports.create = function (config, cb) { allowSubscriptions: config.allowSubscriptions, myDomain: config.myDomain, mySubdomain: config.mySubdomain, - customLimits: config.customLimits, + customLimits: config.customLimits || {}, // FIXME this attribute isn't in the default conf // but it is referenced in Quota domain: config.domain From de6b9e2228c7537f77b3c80688d1468fa1c97d3e Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 10:50:30 -0500 Subject: [PATCH 06/14] apply some defaults when loading the config --- lib/load-config.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/load-config.js b/lib/load-config.js index 1c502226a..0b861b826 100644 --- a/lib/load-config.js +++ b/lib/load-config.js @@ -18,5 +18,23 @@ try { } config = require("../config/config.example"); } + +var isPositiveNumber = function (n) { + return (!isNaN(n) && n >= 0); +}; + +if (!isPositiveNumber(config.inactiveTime)) { + config.inactiveTime = 90; +} +if (!isPositiveNumber(config.archiveRetentionTime)) { + config.archiveRetentionTime = 90; +} +if (!isPositiveNumber(config.maxUploadSize)) { + config.maxUploadSize = 20 * 1024 * 1024; +} +if (!isPositiveNumber(config.defaultStorageLimit)) { + config.defaultStorageLimit = 50 * 1024 * 1024; +} + module.exports = config; From 03ff9bd0d77499be4bdb75efa414533065f85de2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 11:45:22 -0500 Subject: [PATCH 07/14] start deprecating usage of myDomain --- lib/historyKeeper.js | 3 ++- scripts/check-accounts.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index 95cb43e56..1d938ccfd 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -66,7 +66,8 @@ module.exports.create = function (config, cb) { flushCache: config.flushCache, adminEmail: config.adminEmail, allowSubscriptions: config.allowSubscriptions, - myDomain: config.myDomain, + myDomain: config.httpUnsafeOrigin, + // XXX not included in the config... mySubdomain: config.mySubdomain, customLimits: config.customLimits || {}, // FIXME this attribute isn't in the default conf diff --git a/scripts/check-accounts.js b/scripts/check-accounts.js index 4d0067d43..9e75da0c6 100644 --- a/scripts/check-accounts.js +++ b/scripts/check-accounts.js @@ -4,7 +4,7 @@ var Config = require("../lib/load-config"); var Package = require("../package.json"); var body = JSON.stringify({ - domain: Config.myDomain, + domain: Config.myDomain || Config.httpUnsafeOrigin, subdomain: Config.mySubdomain || null, adminEmail: Config.adminEmail, version: Package.version, From e70c3ff0abced42a5c426edd65c4237067258473 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 12:01:52 -0500 Subject: [PATCH 08/14] add some default config values and warn if provided values seem incorrect --- server.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/server.js b/server.js index ddf9fc8b0..d7f663d4e 100644 --- a/server.js +++ b/server.js @@ -8,6 +8,7 @@ var Package = require('./package.json'); var Path = require("path"); var nThen = require("nthen"); var Util = require("./lib/common-util"); +var Default = require("./lib/defaults"); var config = require("./lib/load-config"); @@ -35,6 +36,47 @@ if (process.env.PACKAGE) { FRESH_KEY = +new Date(); } +(function () { + // you absolutely must provide an 'httpUnsafeOrigin' + if (typeof(config.httpUnsafeOrigin) !== 'string') { + throw new Error("No 'httpUnsafeOrigin' provided"); + } + + config.httpUnsafeOrigin = config.httpUnsafeOrigin.trim(); + + // fall back to listening on a local address + // if httpAddress is not a string + if (typeof(config.httpAddress) !== 'string') { + config.httpAddress = '127.0.0.1'; + } + + // listen on port 3000 if a valid port number was not provided + if (typeof(config.httpPort) !== 'number' || config.httpPort > 65535) { + config.httpPort = 3000; + } + + if (typeof(httpSafeOrigin) !== 'string') { + if (typeof(config.httpSafePort) !== 'number') { + config.httpSafePort = config.httpPort + 1; + } + + if (DEV_MODE) { return; } + console.log(` + m m mm mmmmm mm m mmmmm mm m mmm m + # # # ## # "# #"m # # #"m # m" " # + " #"# # # # #mmmm" # #m # # # #m # # mm # + ## ##" #mm# # "m # # # # # # # # # + # # # # # " # ## mm#mm # ## "mmm" # +`); + + console.log("\nNo 'httpSafeOrigin' provided."); + console.log("Your configuration probably isn't taking advantage of all of CryptPad's security features!"); + console.log("This is acceptable for development, otherwise your users may be at risk.\n"); + + console.log("Serving sandboxed content via port %s.\nThis is probably not what you want for a production instance!\n", config.httpSafePort); + } +}()); + var configCache = {}; config.flushCache = function () { configCache = {}; @@ -47,11 +89,21 @@ config.flushCache = function () { const clone = (x) => (JSON.parse(JSON.stringify(x))); var setHeaders = (function () { - if (typeof(config.httpHeaders) !== 'object') { return function () {}; } + // load the default http headers unless the admin has provided their own via the config file + var headers; + + var custom = config.httpHeaders; + // if the admin provided valid http headers then use them + if (custom && typeof(custom) === 'object' && !Array.isArray(custom)) { + headers = clone(custom); + } else { + // otherwise use the default + headers = Default.httpHeaders(); + } - const headers = clone(config.httpHeaders); - if (config.contentSecurity) { - headers['Content-Security-Policy'] = clone(config.contentSecurity); + // next define the base Content Security Policy (CSP) headers + if (typeof(config.contentSecurity) === 'string') { + headers['Content-Security-Policy'] = config.contentSecurity; if (!/;$/.test(headers['Content-Security-Policy'])) { headers['Content-Security-Policy'] += ';' } if (headers['Content-Security-Policy'].indexOf('frame-ancestors') === -1) { // backward compat for those who do not merge the new version of the config @@ -59,10 +111,16 @@ var setHeaders = (function () { // It also fixes the cross-domain iframe. headers['Content-Security-Policy'] += "frame-ancestors *;"; } + } else { + // use the default CSP headers constructed with your domain + headers['Content-Security-Policy'] = Default.contentSecurity(config.httpUnsafeOrigin); } + const padHeaders = clone(headers); - if (config.padContentSecurity) { - padHeaders['Content-Security-Policy'] = clone(config.padContentSecurity); + if (typeof(config.padContentSecurity) === 'string') { + padHeaders['Content-Security-Policy'] = config.padContentSecurity; + } else { + padHeaders['Content-Security-Policy'] = Default.padContentSecurity(config.httpUnsafeOrigin); } if (Object.keys(headers).length) { return function (req, res) { @@ -116,7 +174,7 @@ app.use(Express.static(__dirname + '/www')); // FIXME I think this is a regression caused by a recent PR // correct this hack without breaking the contributor's intended behaviour. -var mainPages = config.mainPages || ['index', 'privacy', 'terms', 'about', 'contact']; +var mainPages = config.mainPages || Default.mainPages(); var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$'); app.get(mainPagePattern, Express.static(__dirname + '/customize')); app.get(mainPagePattern, Express.static(__dirname + '/customize.dist')); @@ -163,7 +221,7 @@ var serveConfig = (function () { removeDonateButton: (config.removeDonateButton === true), allowSubscriptions: (config.allowSubscriptions === true), websocketPath: config.externalWebsocketURL, - httpUnsafeOrigin: config.httpUnsafeOrigin.replace(/^\s*/, ''), + httpUnsafeOrigin: config.httpUnsafeOrigin, adminEmail: config.adminEmail, adminKeys: admins, inactiveTime: config.inactiveTime, From c371a257bfd7ae116b044c53d22512bd6ab90f0e Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 13:17:02 -0500 Subject: [PATCH 09/14] WIP example config reorganization --- config/config.example.js | 79 ++++++++++++---------------------------- 1 file changed, 23 insertions(+), 56 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index a15ea0992..ae5b11702 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -1,46 +1,23 @@ -/* - globals module -*/ -var _domain = 'http://localhost:3000/'; - -// You can `kill -USR2` the node process and it will write out a heap dump. -// If your system doesn't support dumping, comment this out and install with -// `npm install --production` -// See: https://strongloop.github.io/strongloop.com/strongblog/how-to-heap-snapshots/ - -// to enable this feature, uncomment the line below: -// require('heapdump'); +/* globals module */ +module.exports = { + httpUnsafeOrigin: 'http://localhost:3000/', // XXX -// we prepend a space because every usage expects it -// requiring admins to preserve it is unnecessarily confusing -var domain = ' ' + _domain; + // This is for deployment in production, CryptPad uses a separate origin (domain) to host the + // cross-domain iframe. It can simply host the same content as CryptPad. + // httpSafeOrigin: "https://some-other-domain.xyz", -var Default = require("../lib/defaults"); -module.exports = { - /* Sales coming from your server will be identified by your domain - * - * If you are using CryptPad in a business context, please consider taking a support contract - * by contacting sales@cryptpad.fr - */ - myDomain: _domain, // the address you want to bind to, :: means all ipv4 and ipv6 addresses // this may not work on all operating systems - httpAddress: '::', + //httpAddress: '::', // the port on which your httpd will listen - httpPort: 3000, + //httpPort: 3000, // This is for allowing the cross-domain iframe to function when developing httpSafePort: 3001, - // This is for deployment in production, CryptPad uses a separate origin (domain) to host the - // cross-domain iframe. It can simply host the same content as CryptPad. - // httpSafeOrigin: "https://some-other-domain.xyz", - - httpUnsafeOrigin: domain, - /* ===================== * Admin @@ -53,9 +30,11 @@ module.exports = { * which can be found on the settings page for registered users. * Entries should be strings separated by a comma. */ +/* adminKeys: [ //"https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=", ], +*/ /* CryptPad's administration panel includes a "support" tab * wherein administrators with a secret key can view messages @@ -69,6 +48,7 @@ module.exports = { * */ // supportMailboxPublicKey: "", + supportMailboxPublicKey: 'oxuMPm3xXHFALYaeFdAepVZyCpEPNTAPBO8MlpjdQw8=', /* ===================== * Infra setup @@ -86,23 +66,6 @@ module.exports = { */ //externalWebsocketURL: 'wss://api.yourdomain.com/cryptpad_websocket - /* CryptPad can be configured to send customized HTTP Headers - * These settings may vary widely depending on your needs - * Examples are provided below - */ - httpHeaders: Default.httpHeaders(), - - contentSecurity: Default.contentSecurity(domain), - - // CKEditor and OnlyOffice require significantly more lax content security policy in order to function. - padContentSecurity: Default.padContentSecurity(domain), - - /* Main pages - * add exceptions to the router so that we can access /privacy.html - * and other odd pages - */ - mainPages: Default.mainPages(), - /* ===================== * Subscriptions * ===================== */ @@ -122,7 +85,7 @@ module.exports = { * If you chose B, set 'allowSubscriptions' to false. * If you chose C, set 'removeDonateButton' to true */ - allowSubscriptions: true, + //allowSubscriptions: true, removeDonateButton: false, /* @@ -142,7 +105,7 @@ module.exports = { * development. Running a public instance that provides a "better deal" than cryptpad.fr * is effectively using the project against itself. */ - defaultStorageLimit: 50 * 1024 * 1024, + //defaultStorageLimit: 50 * 1024 * 1024, /* * CryptPad allows administrators to give custom limits to their friends. @@ -152,8 +115,8 @@ module.exports = { * * hint: 1GB is 1024 * 1024 * 1024 bytes */ +/* customLimits: { - /* "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { limit: 20 * 1024 * 1024 * 1024, plan: 'insider', @@ -164,8 +127,8 @@ module.exports = { plan: 'insider', note: 'storage space donated by my.awesome.website' } - */ }, +*/ /* ===================== * STORAGE @@ -175,9 +138,11 @@ module.exports = { * after a configurable number of days of inactivity (default 90 days). * The value can be changed or set to false to remove expiration. * Expired pads can then be removed using a cron job calling the - * `delete-inactive.js` script with node + * `evict-inactive.js` script with node + * + * defaults to 90 days if nothing is provided */ - inactiveTime: 90, // days + //inactiveTime: 90, // days /* CryptPad archives some data instead of deleting it outright. * This archived data still takes up space and so you'll probably still want to @@ -190,14 +155,16 @@ module.exports = { * deletion. Set this value to the number of days you'd like to retain * archived data before it's removed permanently. * + * defaults to 15 days if nothing is provided */ - archiveRetentionTime: 15, + //archiveRetentionTime: 15, /* Max Upload Size (bytes) * this sets the maximum size of any one file uploaded to the server. * anything larger than this size will be rejected + * defaults to 20MB if no value is provided */ - maxUploadSize: 20 * 1024 * 1024, + //maxUploadSize: 20 * 1024 * 1024, // XXX premiumUploadSize: 100 * 1024 * 1024, From 92896fb9197c60d7c9aabec6a08360a6f4ceacf6 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 14:24:03 -0500 Subject: [PATCH 10/14] rearrange a big part of the example config file --- config/config.example.js | 212 ++++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 80 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index ae5b11702..273c196d2 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -1,30 +1,100 @@ /* globals module */ -module.exports = { - httpUnsafeOrigin: 'http://localhost:3000/', // XXX - // This is for deployment in production, CryptPad uses a separate origin (domain) to host the - // cross-domain iframe. It can simply host the same content as CryptPad. - // httpSafeOrigin: "https://some-other-domain.xyz", +/* DISCLAIMER: + + There are two recommended methods of running a CryptPad instance: + + 1. Using a standalone nodejs server without HTTPS (suitable for local development) + 2. Using NGINX to serve static assets and to handle HTTPS for API server's websocket traffic + + We do not officially recommend or support Apache, Docker, Kubernetes, Traefik, or any other configuration. + Support requests for such setups should be directed to their authors. + If you're having difficulty difficulty configuring your instance + we suggest that you join the project's IRC/Matrix channel. + If you don't have any difficulty configuring your instance and you'd like to + support us for the work that went into making it pain-free we are quite happy + to accept donations via our opencollective page: https://opencollective.com/cryptpad + +*/ +module.exports = { +/* CryptPad is designed to serve its content over two domains. + * Account passwords and cryptographic content is handled on the 'main' domain, + * while the user interface is loaded on a 'sandbox' domain + * which can only access information which the main domain willingly shares. + * + * In the event of an XSS vulnerability in the UI (that's bad) + * this system prevents attackers from gaining access to your account (that's good). + * + * Most problems with new instances are related to this system blocking access + * because of incorrectly configured sandboxes. If you only see a white screen + * when you try to load CryptPad, this is probably the cause. + * + * PLEASE READ THE FOLLOWING COMMENTS CAREFULLY. + * + */ + +/* httpUnsafeOrigin is the URL that clients will enter to load your instance. + * Any other URL that somehow points to your instance is supposed to be blocked. + * The default provided below assumes you are loading CryptPad from a server + * which is running on the same machine, using port 3000. + * + * In a production instance this should be available ONLY over HTTPS + * using the default port for HTTPS (443) ie. https://cryptpad.fr + * In such a case this should be handled by NGINX, as documented in + * cryptpad/docs/example.nginx.conf (see the $main_domain variable) + * + */ + httpUnsafeOrigin: 'http://localhost:3000/', + +/* httpSafeOrigin is the URL that is used for the 'sandbox' described above. + * If you're testing or developing with CryptPad on your local machine then + * it is appropriate to leave this blank. The default behaviour is to serve + * the main domain over port 3000 and to serve the content over port 3001. + * + * This is not appropriate in a production environment where invasive networks + * may filter traffic going over abnormal ports. + * To correctly configure your production instance you must provide a URL + * with a different domain (a subdomain is sufficient). + * It will be used to load the UI in our 'sandbox' system. + * + * This value corresponds to the $sandbox_domain variable + * in the example nginx file. + * + * CUSTOMIZE AND UNCOMMENT THIS FOR PRODUCTION INSTALLATIONS. + */ + // httpSafeOrigin: "https://some-other-domain.xyz", - // the address you want to bind to, :: means all ipv4 and ipv6 addresses - // this may not work on all operating systems +/* httpAddress specifies the address on which the nodejs server + * should be accessible. By default it will listen on 127.0.0.1 + * (IPv4 localhost on most systems). If you want it to listen on + * all addresses, including IPv6, set this to '::'. + * + */ //httpAddress: '::', - // the port on which your httpd will listen +/* httpPort specifies on which port the nodejs server should listen. + * By default it will serve content over port 3000, which is suitable + * for both local development and for use with the provided nginx example, + * which will proxy websocket traffic to your node server. + * + */ //httpPort: 3000, - // This is for allowing the cross-domain iframe to function when developing - httpSafePort: 3001, - +/* httpSafePort allows you to specify an alternative port from which + * the node process should serve sandboxed assets. The default value is + * that of your httpPort + 1. You probably don't need to change this. + * + */ + //httpSafePort: 3001, /* ===================== * Admin * ===================== */ /* - * CryptPad now contains an administration panel. Its access is restricted to specific + * CryptPad contains an administration panel. Its access is restricted to specific * users using the following list. * To give access to the admin panel to a user account, just add their user id, * which can be found on the settings page for registered users. @@ -48,87 +118,42 @@ module.exports = { * */ // supportMailboxPublicKey: "", - supportMailboxPublicKey: 'oxuMPm3xXHFALYaeFdAepVZyCpEPNTAPBO8MlpjdQw8=', - /* ===================== - * Infra setup - * ===================== */ - - /* Your CryptPad server will share this value with clients - * via its /api/config endpoint. + /* We're very proud that CryptPad is available to the public as free software! + * We do, however, still need to pay our bills as we develop the platform. * - * If you want to host your API and asset servers on different hosts - * specify a URL for your API server websocket endpoint, like so: - * wss://api.yourdomain.com/cryptpad_websocket + * By default CryptPad will prompt users to consider donating to + * our OpenCollective campaign. We publish the state of our finances periodically + * so you can decide for yourself whether our expenses are reasonable. * - * Otherwise, leave this commented and your clients will use the default - * websocket (wss://yourdomain.com/cryptpad_websocket) + * You can disable any solicitations for donations by setting 'removeDonateButton' to true, + * but we'd appreciate it if you didn't! */ - //externalWebsocketURL: 'wss://api.yourdomain.com/cryptpad_websocket + //removeDonateButton: false, - /* ===================== - * Subscriptions - * ===================== */ - - /* Limits, Donations, Subscriptions and Contact - * - * By default, CryptPad limits every registered user to 50MB of storage. It also shows a - * subscribe button which allows them to upgrade to a paid account. We handle payment, - * and keep 50% of the proceeds to fund ongoing development. - * - * You can: - * A: leave things as they are - * B: disable accounts but display a donate button - * C: hide any reference to paid accounts or donation - * - * If you chose A then there's nothing to do. - * If you chose B, set 'allowSubscriptions' to false. - * If you chose C, set 'removeDonateButton' to true - */ - //allowSubscriptions: true, - removeDonateButton: false, - - /* - * By default, CryptPad also contacts our accounts server once a day to check for changes in - * the people who have accounts. This check-in will also send the version of your CryptPad - * instance and your email so we can reach you if we are aware of a serious problem. We will - * never sell it or send you marketing mail. If you want to block this check-in and remain - * completely invisible, set this and allowSubscriptions both to false. + /* CryptPad will display a point of contact for your instance on its contact page + * (/contact.html) if you provide it below. */ adminEmail: 'i.did.not.read.my.config@cryptpad.fr', /* - * If you are using CryptPad internally and you want to increase the per-user storage limit, - * change the following value. + * By default, CryptPad contacts one of our servers once a day. + * This check-in will also send some very basic information about your instance including its + * version and the adminEmail so we can reach you if we are aware of a serious problem. + * We will never sell it or send you marketing mail. * - * Please note: This limit is what makes people subscribe and what pays for CryptPad - * development. Running a public instance that provides a "better deal" than cryptpad.fr - * is effectively using the project against itself. + * If you want to block this check-in and remain set 'blockDailyCheck' to true. */ - //defaultStorageLimit: 50 * 1024 * 1024, + //blockDailyCheck: false, /* - * CryptPad allows administrators to give custom limits to their friends. - * add an entry for each friend, identified by their user id, - * which can be found on the settings page. Include a 'limit' (number of bytes), - * a 'plan' (string), and a 'note' (string). + * By default users get 50MB of storage by registering on an instance. + * You can set this value to whatever you want. * - * hint: 1GB is 1024 * 1024 * 1024 bytes + * hint: 50MB is 50 * 1024 * 1024 */ -/* - customLimits: { - "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { - limit: 20 * 1024 * 1024 * 1024, - plan: 'insider', - note: 'storage space donated by my.awesome.website' - }, - "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": { - limit: 10 * 1024 * 1024 * 1024, - plan: 'insider', - note: 'storage space donated by my.awesome.website' - } - }, -*/ + //defaultStorageLimit: 50 * 1024 * 1024, + /* ===================== * STORAGE @@ -166,8 +191,35 @@ module.exports = { */ //maxUploadSize: 20 * 1024 * 1024, - // XXX - premiumUploadSize: 100 * 1024 * 1024, + /* + * CryptPad allows administrators to give custom limits to their friends. + * add an entry for each friend, identified by their user id, + * which can be found on the settings page. Include a 'limit' (number of bytes), + * a 'plan' (string), and a 'note' (string). + * + * hint: 1GB is 1024 * 1024 * 1024 bytes + */ +/* + customLimits: { + "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { + limit: 20 * 1024 * 1024 * 1024, + plan: 'insider', + note: 'storage space donated by my.awesome.website' + }, + "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": { + limit: 10 * 1024 * 1024 * 1024, + plan: 'insider', + note: 'storage space donated by my.awesome.website' + } + }, +*/ + + /* Users with premium accounts (those with a plan included in their customLimit) + * can benefit from an increased upload size limit. By default they are restricted to the same + * upload size as any other registered user. + * + */ + //premiumUploadSize: 100 * 1024 * 1024, /* ===================== * DATABASE VOLUMES From 2f00ff327861b2d26b0d426e4669d2d135831991 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 28 Feb 2020 14:24:53 -0500 Subject: [PATCH 11/14] simplify check-in deactivation --- lib/commands/quota.js | 1 + lib/historyKeeper.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/commands/quota.js b/lib/commands/quota.js index 74c4eca44..72d04cc68 100644 --- a/lib/commands/quota.js +++ b/lib/commands/quota.js @@ -38,6 +38,7 @@ Quota.updateCachedLimits = function (Env, cb) { if (Env.adminEmail === false) { Quota.applyCustomLimits(Env); if (Env.allowSubscriptions === false) { return; } + if (Env.blockDailyCheck === true) { return; } throw new Error("allowSubscriptions must be false if adminEmail is false"); } diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index 1d938ccfd..3a27b228a 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -65,7 +65,9 @@ module.exports.create = function (config, cb) { WARN: WARN, flushCache: config.flushCache, adminEmail: config.adminEmail, - allowSubscriptions: config.allowSubscriptions, + allowSubscriptions: config.allowSubscriptions === true, + blockDailyCheck: config.blockDailyCheck === true, + myDomain: config.httpUnsafeOrigin, // XXX not included in the config... mySubdomain: config.mySubdomain, From cd229684678ccb8088a0cb71c63982dcd373e089 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 2 Mar 2020 16:33:03 +0100 Subject: [PATCH 12/14] Translated using Weblate (Catalan) Currently translated at 50.9% (618 of 1215 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/ca/ --- www/common/translations/messages.ca.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/www/common/translations/messages.ca.json b/www/common/translations/messages.ca.json index b22340b05..aca3b8068 100644 --- a/www/common/translations/messages.ca.json +++ b/www/common/translations/messages.ca.json @@ -536,8 +536,8 @@ "settings_creationSkipFalse": "Mostra", "settings_templateSkip": "Salta la finestra de selecció de plantilla", "settings_templateSkipHint": "Quan genereu un document nou buit, si teniu desades plantilles per aquest tipus de document, apareix una finestra preguntant-vos si voleu utilitzar una plantilla. Aquí podeu triar si no voleu veure mai més la finestra i no utilitzar una plantilla.", - "settings_ownDriveTitle": "Habilita les darreres funcionalitats del compte", - "settings_ownDriveHint": "Per raons tècniques, els comptes antics no tenen accés a totes les funcionalitats noves. Si feu una actualització a un compte nou, preparareu el vostre CryptDrive per les properes funcionalitats sense interrompre la vostra activitat habitual.", + "settings_ownDriveTitle": "Milloreu el compte", + "settings_ownDriveHint": "Degut a raons tècniques els comptes antics no tenen accés a les darreres funcionalitats . Una actualització gratuïta habilitarà les funcionalitats actuals i prepararà el vostre CryptDrive per futures actualitzacions.", "settings_ownDriveButton": "Milloreu el vostre compte", "settings_ownDriveConfirm": "Millorar el vostre compte porta una estona. Necessitareu tornar-vos a connectar en tots els vostres dispositius. Segur que ho voleu fer?", "settings_ownDrivePending": "El vostre compte s'està posant al dia. No tanqueu ni torneu a carregar aquesta pàgina fins que el procés hagi acabat.", @@ -616,5 +616,8 @@ "download_step2": "Desxifrant", "download_step1": "Descarregant", "download_dl": "Descarrega", - "download_resourceNotAvailable": "El recurs sol·licitat no estava disponible... Premeu Esc per continuar." + "download_resourceNotAvailable": "El recurs sol·licitat no estava disponible... Premeu Esc per continuar.", + "about_contributors": "Col·laboracions clau", + "about_core": "Desenvolupament principal", + "about_intro": "CryptPad s'ha creat dins l'Equip de Recerca de XWiki SAS, una petita empresa de París, França i Iasi, Romania. Hi ha 3 membres de l'equip central treballant amb CryptPad més una quantitat de persones col·laboradores, dins i fora d'XWiki SAS." } From e240c3d29957f6029ac9e8c67a5491513bfb21ba Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 2 Mar 2020 16:33:03 +0100 Subject: [PATCH 13/14] Translated using Weblate (French) Currently translated at 100.0% (1227 of 1227 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 34bfec27b..038b6be2a 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -554,7 +554,7 @@ "upload_success": "Votre fichier ({0}) a été importé avec succès et ajouté à votre CryptDrive.", "upload_notEnoughSpace": "Il n'y a pas assez d'espace libre dans votre CryptDrive pour ce fichier.", "upload_notEnoughSpaceBrief": "Pas assez d'espace", - "upload_tooLarge": "Ce fichier dépasse la taille maximale autorisée.", + "upload_tooLarge": "Ce fichier dépasse la taille maximale autorisée pour votre compte.", "upload_tooLargeBrief": "Fichier trop volumineux", "upload_choose": "Choisir un fichier", "upload_pending": "En attente", @@ -1142,10 +1142,10 @@ "register_emailWarning1": "Vous pouvez continuer, mais ces données ne sont pas nécessaires et ne seront pas envoyées à notre serveur.", "register_emailWarning2": "Vous ne pourrez pas réinitialiser votre mot de passe en utilisant votre adresse email comme sur beaucoup d'autres services.", "register_emailWarning3": "Si vous souhaitez tout de même utiliser votre adresse email comme nom d'utilisateur, appuyez sur OK.", - "owner_removeText": "Supprimer un propriétaire existant", - "owner_removePendingText": "Annuler une offre en attente", + "owner_removeText": "Propriétaires", + "owner_removePendingText": "En attente", "owner_addText": "Proposer à un contact d'être co-propriétaire de ce document", - "owner_unknownUser": "Utilisateur inconnu", + "owner_unknownUser": "?????", "owner_removeButton": "Supprimer les propriétaires sélectionnés", "owner_removePendingButton": "Annuler les offres sélectionnées", "owner_addButton": "Proposer d'être propriétaire", @@ -1311,5 +1311,17 @@ "historyTrim_historySize": "Historique : {0}", "areYouSure": "Êtes-vous sûr ?", "copy_title": "{0} (copie)", - "makeACopy": "Créer une copie" + "makeACopy": "Créer une copie", + "owner_text": "Le(s) propriétaire(s) d'un pad sont les seuls utilisateurs autorisés à : ajouter/supprimer des propriétaires, restreindre l'accès au bloc-notes avec une liste d'accès, ou à supprimer le pad.", + "access_muteRequests": "Masquer les requêtes d'accès pour ce pad", + "allow_label": "Liste d'accès : {0}", + "allow_disabled": "désactivée", + "allow_enabled": "activée", + "allow_checkbox": "L'utilisation d'une liste d'accès signifie que seuls les utilisateurs et propriétaires sélectionnés pourront accéder à ce document.", + "access_noContact": "Il n'y a plus de contacts à ajouter", + "contacts": "Contacts", + "restrictedError": "Vous n'êtes pas autorisé à accéder à ce document", + "accessButton": "Accès", + "access_allow": "Liste", + "access_main": "Accès" } From 4cdee4e973bbe97c406690c1a05b84734351b3af Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 2 Mar 2020 16:33:03 +0100 Subject: [PATCH 14/14] Translated using Weblate (English) Currently translated at 100.0% (1227 of 1227 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1227 of 1227 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1226 of 1226 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1225 of 1225 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1224 of 1224 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1223 of 1223 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1222 of 1222 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1221 of 1221 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1220 of 1220 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1219 of 1219 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1218 of 1218 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1217 of 1217 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1216 of 1216 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 25b1c58f0..e68e56164 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -571,7 +571,7 @@ "upload_success": "Your file ({0}) has been successfully uploaded and added to your drive.", "upload_notEnoughSpace": "There is not enough space for this file in your CryptDrive.", "upload_notEnoughSpaceBrief": "Not enough space", - "upload_tooLarge": "This file exceeds the maximum upload size.", + "upload_tooLarge": "This file exceeds the maximum upload size allowed for your account.", "upload_tooLargeBrief": "File too large", "upload_choose": "Choose a file", "upload_pending": "Pending", @@ -1147,10 +1147,10 @@ "features_noData": "No personal information required", "features_pricing": "Between {0} and {2}€ per month", "features_emailRequired": "Email address required", - "owner_removeText": "Remove an existing owner", - "owner_removePendingText": "Cancel a pending offer", + "owner_removeText": "Owners", + "owner_removePendingText": "Pending", "owner_addText": "Offer co-ownership to a contact", - "owner_unknownUser": "Unknown user", + "owner_unknownUser": "?????", "owner_removeButton": "Remove selected owners", "owner_removePendingButton": "Cancel selected offers", "owner_addButton": "Offer ownership", @@ -1311,5 +1311,17 @@ "settings_trimHistoryTitle": "Delete History", "settings_trimHistoryHint": "Save storage space by deleting the history of your drive and notifications. This will not affect the history of your pads. You can delete the history of pads in their properties dialog.", "makeACopy": "Make a copy", - "copy_title": "{0} (copy)" + "copy_title": "{0} (copy)", + "access_main": "Access", + "access_allow": "List", + "accessButton": "Access", + "restrictedError": "You are not authorised to access this document", + "contacts": "Contacts", + "access_noContact": "No other contact to add", + "allow_checkbox": "Using an access list means that only selected users and owners will be able to access this document.", + "allow_enabled": "enabled", + "allow_disabled": "disabled", + "allow_label": "Access list: {0}", + "access_muteRequests": "Mute access requests for this pad", + "owner_text": "The owner(s) of a pad are the only users authorised to: add/remove owners, restrict access to the pad with an access list, or delete the pad." }