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 <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson
2025-11-14 01:20:00 +01:00
parent 78cac1526f
commit 6d970dbafd
9 changed files with 49 additions and 16 deletions

View File

@@ -117,13 +117,14 @@ 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). |
| `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`. |

View File

@@ -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

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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

View File

@@ -2,6 +2,11 @@
## <i class="fa fa-tag"></i> 1.x.x <i class="fa fa-calendar-o"></i> 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

View File

@@ -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)

View File

@@ -3,6 +3,7 @@ window.urlpath = '<%- urlpath %>'
window.debug = <%- debug %>
window.version = '<%- version %>'
window.enableUploads = '<%- enableUploads %>'
window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'