From 6d970dbafdf906ece5c7aaae840ca2a0fcb9961e Mon Sep 17 00:00:00 2001 From: Erik Michelson Date: Fri, 14 Nov 2025 01:20:00 +0100 Subject: [PATCH] feat(config): allow restriction and disabling of uploads Previously, image uploads were always allowed, unless `CMD_ALLOW_ANONYMOUS=false` and `CMD_ALLOW_ANONYMOUS_EDITS=false`. This PR adds a new config option `CMD_ENABLE_UPLOADS` to configure image uploads independently. There are three different modes: `all` (everyone can upload, guests too), `registered` (only registered and logged-in users can upload images), and `none` to completely disable image uploads. The default value is non-breaking as it is `all`, unless the config `CMD_ALLOW_ANONYMOUS=false` and `CMD_ALLOW_ANONYMOUS_EDITS=false` is set, in which case the value is `registered`. The UI will reflect the setting and either show or hide the upload button. Signed-off-by: Erik Michelson --- docs/content/configuration.md | 23 ++++++++++++----------- lib/config/default.js | 5 +++++ lib/config/environment.js | 1 + lib/config/index.js | 11 +++++++++++ lib/web/imageRouter/index.js | 12 ++++++++---- lib/web/statusRouter.js | 3 ++- public/docs/release-notes.md | 5 +++++ public/js/index.js | 4 ++++ public/js/lib/common/constant.ejs | 1 + 9 files changed, 49 insertions(+), 16 deletions(-) diff --git a/docs/content/configuration.md b/docs/content/configuration.md index 04bc611e..43c9d607 100644 --- a/docs/content/configuration.md +++ b/docs/content/configuration.md @@ -116,17 +116,18 @@ these are rarely used for various reasons. ## Users and Privileges -| config file | environment | **default** and example value | description | -|--------------------------------|--------------------------------------|-------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `allowAnonymous` | `CMD_ALLOW_ANONYMOUS` | **`true`** or `false` | Set to allow anonymous usage (default is `true`). | -| `allowAnonymousEdits` | `CMD_ALLOW_ANONYMOUS_EDITS` | **`false`** or `true` | If `allowAnonymous` is `false`: allow users to select `freely` permission, allowing guests to edit existing notes (default is `false`). | -| `allowFreeURL` | `CMD_ALLOW_FREEURL` | **`false`** or `true` | Set to allow new note creation by accessing a nonexistent note URL. This is the behavior familiar from [Etherpad](https://github.com/ether/etherpad-lite). | -| `requireFreeURLAuthentication` | `CMD_REQUIRE_FREEURL_AUTHENTICATION` | **`false`** or `true` | Set to require authentication for FreeURL mode style note creation. | -| `disableNoteCreation` | `CMD_DISABLE_NOTE_CREATION` | **`false`** or `true` | Set to `true` to disallow any person to create notes. | -| `defaultPermission` | `CMD_DEFAULT_PERMISSION` | **`editable`**, `freely`, `limited`, `locked`, `protected` or `private` | Set notes default permission (only applied on signed-in users). | -| `sessionName` | | **`connect.sid`** | Cookie session name. | -| `sessionLife` | `CMD_SESSION_LIFE` | **`14 * 24 * 60 * 60 * 1000`**, `1209600000` (14 days) | Cookie session life time in milliseconds. | -| `sessionSecret` | `CMD_SESSION_SECRET` | **`secret`** | Cookie session secret used to sign the session cookie. If none is set, one will randomly generated on each startup, meaning all your users will be logged out. Can be generated with e.g. `pwgen -s 64 1`. | +| config file | environment | **default** and example value | description | +|--------------------------------|--------------------------------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `allowAnonymous` | `CMD_ALLOW_ANONYMOUS` | **`true`** or `false` | Set to allow anonymous usage (default is `true`). | +| `allowAnonymousEdits` | `CMD_ALLOW_ANONYMOUS_EDITS` | **`false`** or `true` | If `allowAnonymous` is `false`: allow users to select `freely` permission, allowing guests to edit existing notes (default is `false`). | +| `allowFreeURL` | `CMD_ALLOW_FREEURL` | **`false`** or `true` | Set to allow new note creation by accessing a nonexistent note URL. This is the behavior familiar from [Etherpad](https://github.com/ether/etherpad-lite). | +| `requireFreeURLAuthentication` | `CMD_REQUIRE_FREEURL_AUTHENTICATION` | **`false`** or `true` | Set to require authentication for FreeURL mode style note creation. | +| `disableNoteCreation` | `CMD_DISABLE_NOTE_CREATION` | **`false`** or `true` | Set to `true` to disallow any person to create notes. | +| `defaultPermission` | `CMD_DEFAULT_PERMISSION` | **`editable`**, `freely`, `limited`, `locked`, `protected` or `private` | Set notes default permission (only applied on signed-in users). | +| `enableUploads` | `CMD_ENABLE_UPLOADS` | **`all`** , `registered`, `none` | Set to specify who can upload images (`all` includes guest users, `registered` restricts to registered users, `none` disables uploads completely). If not set, but both `allowAnonymous` and `allowAnonymousEdits` are `false`, this will be set to `registered`. | +| `sessionName` | | **`connect.sid`** | Cookie session name. | +| `sessionLife` | `CMD_SESSION_LIFE` | **`14 * 24 * 60 * 60 * 1000`**, `1209600000` (14 days) | Cookie session life time in milliseconds. | +| `sessionSecret` | `CMD_SESSION_SECRET` | **`secret`** | Cookie session secret used to sign the session cookie. If none is set, one will randomly generated on each startup, meaning all your users will be logged out. Can be generated with e.g. `pwgen -s 64 1`. | ## Login methods diff --git a/lib/config/default.js b/lib/config/default.js index e57a57b3..3110119b 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -32,6 +32,7 @@ module.exports = { rateLimitNewNotes: 20, cookiePolicy: 'lax', protocolUseSSL: false, + // permissions allowAnonymous: true, allowAnonymousEdits: false, allowFreeURL: false, @@ -39,6 +40,10 @@ module.exports = { disableNoteCreation: false, forbiddenNoteIDs: ['robots.txt', 'favicon.ico', 'api', 'build', 'css', 'docs', 'fonts', 'js', 'uploads', 'vendor', 'views'], defaultPermission: 'editable', + + enableUploads: undefined, // 'all', 'registered', 'none' are valid options. + // This is undefined by default and set during runtime based on allowAnonymous and allowAnonymousEdits for backwards-compatibility unless explicitly set. + dbURL: '', db: {}, // ssl path diff --git a/lib/config/environment.js b/lib/config/environment.js index a90dcef6..6b9f8312 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -35,6 +35,7 @@ module.exports = { allowFreeURL: toBooleanConfig(process.env.CMD_ALLOW_FREEURL), requireFreeURLAuthentication: toBooleanConfig(process.env.CMD_REQUIRE_FREEURL_AUTHENTICATION), disableNoteCreation: toBooleanConfig(process.env.CMD_DISABLE_NOTE_CREATION), + enableUploads: process.env.CMD_ENABLE_UPLOADS, forbiddenNoteIDs: toArrayConfig(process.env.CMD_FORBIDDEN_NOTE_IDS), defaultPermission: process.env.CMD_DEFAULT_PERMISSION, dbURL: process.env.CMD_DB_URL, diff --git a/lib/config/index.js b/lib/config/index.js index e0ab5151..09a14f0d 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -79,6 +79,17 @@ if (!config.allowAnonymous && !config.allowAnonymousEdits) { if (!(config.defaultPermission in config.permission)) { config.defaultPermission = config.permission.editable } +if (config.enableUploads === undefined) { + if (!config.allowAnonymousEdits && !config.allowAnonymous) { + config.enableUploads = 'registered' + } else { + config.enableUploads = 'all' + } +} +if (!['all', 'registered', 'none'].includes(config.enableUploads)) { + logger.error('Config option "enableUploads"/CMD_ENABLE_UPLOADS is not correctly set. Please use "all", "registered" or "none". Defaulting to "all"') + config.enableUploads = 'all' +} // Use HTTPS protocol if the internal TLS server is enabled if (config.useSSL === true) { diff --git a/lib/web/imageRouter/index.js b/lib/web/imageRouter/index.js index d9964827..484b04ec 100644 --- a/lib/web/imageRouter/index.js +++ b/lib/web/imageRouter/index.js @@ -57,13 +57,17 @@ async function checkUploadType (filePath) { // upload image imageRouter.post('/uploadimage', function (req, res) { + const uploadsEnabled = config.enableUploads + if (uploadsEnabled === 'none') { + logger.error('Image upload error: Uploads are disabled') + return errors.errorForbidden(res) + } if ( - !req.isAuthenticated() && - !config.allowAnonymous && - !config.allowAnonymousEdits + uploadsEnabled === 'registered' && + !req.isAuthenticated() ) { logger.error( - 'Image upload error: Anonymous edits and therefore uploads are not allowed' + 'Image upload error: Anonymous uploads are not allowed' ) return errors.errorForbidden(res) } diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js index a807eee2..73540398 100644 --- a/lib/web/statusRouter.js +++ b/lib/web/statusRouter.js @@ -111,7 +111,8 @@ statusRouter.get('/config', function (req, res) { DROPBOX_APP_KEY: config.dropbox.appKey, allowedUploadMimeTypes: config.allowedUploadMimeTypes, linkifyHeaderStyle: config.linkifyHeaderStyle, - cookiePolicy: config.cookiePolicy + cookiePolicy: config.cookiePolicy, + enableUploads: config.enableUploads } res.set({ 'Cache-Control': 'private', // only cache by client diff --git a/public/docs/release-notes.md b/public/docs/release-notes.md index 402ccf16..0e7d2dc8 100644 --- a/public/docs/release-notes.md +++ b/public/docs/release-notes.md @@ -2,6 +2,11 @@ ## 1.x.x UNRELEASED +### Enhancements + +- Add `enableUploads` (`CMD_ENABLE_UPLOADS`) config option to restrict uploads to `registered` users, `all` users or + `none` to completely disable uploads. + ### Bugfixes - Ignore the healthcheck endpoint in the "too busy" limiter diff --git a/public/js/index.js b/public/js/index.js index bf9f09bc..fe4ac85d 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1079,6 +1079,10 @@ function changeMode (type) { // add and update tool bar if (!editorInstance.toolBar) { editorInstance.addToolBar() + const uploadButtonVisible = window.enableUploads === 'all' || (window.enableUploads === 'registered' && personalInfo.login) + if (!uploadButtonVisible) { + $('#uploadImage').remove() + } } // work around foldGutter might not init properly editor.setOption('foldGutter', false) diff --git a/public/js/lib/common/constant.ejs b/public/js/lib/common/constant.ejs index 2a32c333..e377bdf2 100644 --- a/public/js/lib/common/constant.ejs +++ b/public/js/lib/common/constant.ejs @@ -3,6 +3,7 @@ window.urlpath = '<%- urlpath %>' window.debug = <%- debug %> window.version = '<%- version %>' +window.enableUploads = '<%- enableUploads %>' window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %> window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'